AnalogMeter.cpp
上传用户:haifei
上传日期:2022-07-20
资源大小:77k
文件大小:16k
源码类别:

网络编程

开发平台:

Visual C++

  1. // AnalogMeter.cpp : implementation file
  2. // Download by http://www.codefans.net
  3. #include "stdafx.h"
  4. #include <math.h>
  5. #include "AnalogMeter.h"
  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #undef THIS_FILE
  9. static char THIS_FILE[] = __FILE__;
  10. #endif
  11. #define ROUND(x) (int)((x) + 0.5 - (double)((x) < 0))
  12. /////////////////////////////////////////////////////////////////////////////
  13. // CAnalogMeter
  14. IMPLEMENT_DYNCREATE(CAnalogMeter, CCmdTarget)
  15. CAnalogMeter::CAnalogMeter()
  16. {
  17. m_dPI = 4.0*atan(1.0) ;  // for trig calculations
  18. // initialized rectangle locations, will be modified on first drawing
  19. m_rectDraw = CRect(0, 0, 0, 0) ;
  20. m_nRectWidth = 0;
  21. m_nRectHeight = 0;
  22. // draw the whole thing the first time 
  23. m_boolForceRedraw = TRUE ;
  24. m_dRadiansPerValue = 0.0 ;  // will be modified on first drawing
  25. // FALSE if we are printing
  26. m_boolUseBitmaps = TRUE ;
  27. // default titles, scaling and needle position
  28. m_dMinScale = -10.0 ;       
  29. m_dMaxScale = 10.0 ; 
  30. m_dNeedlePos = - 4*atan(1.0)*45/180.0;  //0.0 ;
  31.     m_dPercent = 0;
  32. m_strTitle.Format("Volts") ;
  33. // for numerical values
  34. m_nRangeDecimals = 1 ;
  35. m_nValueDecimals = 3 ;
  36. // grid color
  37. m_colorGrid = RGB(128, 128, 128) ;
  38. // current numerical value color
  39. m_colorValue = RGB(0, 0, 0) ;
  40. // needle color
  41. m_colorNeedle = RGB(255, 0, 0) ;
  42. // background color brushes (for erasing)
  43. m_brushErase.CreateSolidBrush(RGB(255, 255, 255)) ;
  44. m_penErase.CreatePen(PS_SOLID, 0, RGB(255, 255, 255)) ;
  45. }
  46. CAnalogMeter::~CAnalogMeter()
  47. {
  48. m_dcGrid.SelectObject(m_pbitmapOldGrid) ;
  49. m_dcGrid.DeleteDC() ;
  50. m_dcNeedle.SelectObject(m_pbitmapOldNeedle) ;
  51. m_dcNeedle.DeleteDC() ;
  52. m_bitmapGrid.DeleteObject() ;
  53. m_bitmapNeedle.DeleteObject() ;
  54. }
  55. BEGIN_MESSAGE_MAP(CAnalogMeter, CCmdTarget)
  56. //{{AFX_MSG_MAP(CAnalogMeter)
  57. // NOTE - the ClassWizard will add and remove mapping macros here.
  58. //}}AFX_MSG_MAP
  59. END_MESSAGE_MAP()
  60. /////////////////////////////////////////////////////////////////////////////
  61. // CAnalogMeter message handlers
  62. void CAnalogMeter::ShowMeter(CDC * pDC, CRect rectBorder)
  63. {
  64. // check for a new meter or a resize of the old one.
  65. // (if the rectangles have changed, then redraw from scratch).
  66. // If we are printing, always draw from scratch without bitmaps.
  67. if (m_rectOwner != rectBorder) 
  68. m_boolForceRedraw = TRUE ;
  69. if (m_boolForceRedraw || (pDC->IsPrinting()))
  70. {
  71. m_boolForceRedraw = FALSE ;
  72. // first store the rectangle for the owner
  73. // and determine the rectangle to draw to
  74. m_rectOwner = rectBorder ;
  75. if (pDC->IsPrinting())  
  76. {
  77. m_boolUseBitmaps = FALSE ;
  78. m_rectDraw = m_rectOwner ;  // draw directly to the owner
  79. }
  80. else  
  81. {
  82. m_boolUseBitmaps = TRUE ;
  83. m_rectDraw.left = 0 ;       // draw to a bitmap rectangle
  84. m_rectDraw.top = 0 ;
  85. m_rectDraw.right = rectBorder.Width() ;
  86. m_rectDraw.bottom = rectBorder.Height() ;
  87. }
  88. m_nRectWidth = m_rectDraw.Width() ;
  89. m_nRectHeight = m_rectDraw.Height() ;
  90. // if we already have a memory dc, destroy it 
  91. // (this occurs for a re-size of the meter)
  92. if (m_dcGrid.GetSafeHdc())
  93. {
  94. m_dcGrid.SelectObject(m_pbitmapOldGrid) ;
  95. m_dcGrid.DeleteDC() ;
  96. m_dcNeedle.SelectObject(m_pbitmapOldNeedle) ;
  97. m_dcNeedle.DeleteDC() ;
  98. m_bitmapGrid.DeleteObject() ;
  99. m_bitmapNeedle.DeleteObject() ;
  100. }
  101. if (m_boolUseBitmaps)  
  102. {
  103. // create a memory based dc for drawing the grid
  104. m_dcGrid.CreateCompatibleDC(pDC) ;
  105. m_bitmapGrid.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ;
  106. m_pbitmapOldGrid = m_dcGrid.SelectObject(&m_bitmapGrid) ;
  107. // create a memory based dc for drawing the needle
  108. m_dcNeedle.CreateCompatibleDC(pDC) ;
  109. m_bitmapNeedle.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ;
  110. m_pbitmapOldNeedle = m_dcNeedle.SelectObject(&m_bitmapNeedle) ;
  111. }
  112. else  // no bitmaps, draw to the destination
  113. {
  114. // use the destination dc for the grid
  115. m_dcGrid.m_hDC = pDC->m_hDC ;
  116. m_dcGrid.m_hAttribDC = pDC->m_hAttribDC ;
  117. // use the destination dc for the grid
  118. m_dcNeedle.m_hDC = pDC->m_hDC ;
  119. m_dcNeedle.m_hAttribDC = pDC->m_hAttribDC ;
  120. }
  121. // draw the grid in the to the "grid dc"
  122. DrawGrid () ;           
  123. // draw the needle in the "needle dc" 
  124. DrawNeedle () ;    
  125. }
  126. // display the new image, combining the needle with the grid
  127. if (m_boolUseBitmaps)
  128. ShowMeterImage(pDC);
  129. } // end ShowMeter
  130. void CAnalogMeter::ShowMeterImage(CDC *pDC)
  131. {
  132. CDC memDC ;
  133. CBitmap memBitmap ;
  134. CBitmap* oldBitmap ; // bitmap originally found in CMemDC
  135. // this function is only used when the needle and grid
  136. // have been drawn to bitmaps and they need to be combined
  137. // and sent to the destination
  138. if (!m_boolUseBitmaps)  
  139. return ;
  140. // to avoid flicker, establish a memory dc, draw to it 
  141. // and then BitBlt it to the destination "pDC"
  142. memDC.CreateCompatibleDC(pDC) ;
  143. memBitmap.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ;
  144. oldBitmap = (CBitmap *)memDC.SelectObject(&memBitmap) ;
  145. // make sure we have the bitmaps
  146. if (!m_dcGrid.GetSafeHdc())
  147. return ;
  148. if (!m_dcNeedle.GetSafeHdc())
  149. return ;
  150. if (memDC.GetSafeHdc() != NULL)
  151. {
  152. // draw the inverted grid
  153. memDC.BitBlt(0, 0, m_nRectWidth, m_nRectHeight, &m_dcGrid, 0, 0, NOTSRCCOPY) ;
  154. // merge the needle image with the grid
  155. memDC.BitBlt(0, 0, m_nRectWidth, m_nRectHeight, &m_dcNeedle, 0, 0, SRCINVERT) ;
  156. // copy the resulting bitmap to the destination
  157. pDC->BitBlt(m_rectOwner.left, m_rectOwner.top, m_nRectWidth, m_nRectHeight, 
  158.     &memDC, 0, 0, SRCCOPY) ;
  159. }
  160. memDC.SelectObject(oldBitmap) ;
  161. } // end ShowMeterImage
  162. //////////////////////////////////////////////////////
  163. void CAnalogMeter::UpdateNeedle(CDC *pDC, double dPos)
  164. {
  165. // do not support updates if we are not working with 
  166. // bitmaps images
  167. if (!m_boolUseBitmaps)
  168. return ;
  169. // must have created the grid if we are going to 
  170. // update the needle (the needle locations are 
  171. // calculateed based on the grid)
  172. if (!m_dcGrid.GetSafeHdc())
  173. return ;
  174.     m_dPercent = dPos;
  175.     dPos = 2.0*m_dLimitAngleRad*dPos/100.0 - 3.14159*45/180.0;   
  176. // dPos = dPos*3.14159/180.0 ;
  177. // if the needle hasn't changed, don't bother updating
  178. if (m_dNeedlePos == dPos)
  179. return ;
  180. // store the position in the member variable 
  181. // for availability elsewhere
  182. m_dNeedlePos = dPos ;
  183. // draw the new needle image
  184. DrawNeedle () ;
  185. // combine the needle with the grid and display the result
  186. ShowMeterImage (pDC) ;
  187. } // end UpdateNeedle
  188. //////////////////////////////////////////
  189. //
  190. //  画刻度盘
  191. //
  192. void CAnalogMeter::DrawGrid () 
  193. {
  194. int nFontHeight ;
  195. int nLeftBoundX, nRightBoundX,
  196. nLeftBoundY, nRightBoundY ;
  197. double dLimitAngleDeg = 45.0 ;  // this specifies the width of the pie slice
  198. double dX, dY, dTemp ;
  199. CPen penSolid, *oldPen ;
  200. CBrush brushSolid, *oldBrush ;
  201. CFont *oldFont ;
  202. CString tempString ;
  203. // draw the boundary rectangle and 
  204. // fill the entire area with the background color
  205. penSolid.CreatePen(PS_SOLID, 0, RGB(0, 0, 0)) ;
  206. oldPen = m_dcGrid.SelectObject(&penSolid) ;
  207. oldBrush = m_dcGrid.SelectObject(&m_brushErase) ;
  208. m_dcGrid.Rectangle(m_rectDraw) ;
  209. m_dcGrid.SelectObject(oldBrush) ;
  210. m_dcGrid.SelectObject(oldPen) ;
  211. // determine the angular scaling
  212. m_dLimitAngleRad = dLimitAngleDeg*m_dPI/180.0 ;
  213. m_dRadiansPerValue = (2.0*m_dLimitAngleRad)/(m_dMaxScale-m_dMinScale) ;
  214. // determine the center point
  215. m_nCXPix = (m_rectDraw.left+m_rectDraw.right)/2 ;
  216. m_nCYPix = m_rectDraw.bottom - m_nRectHeight/5 ;
  217. // determine the size and location of the meter "pie"
  218. m_nRadiusPix = m_nRectWidth*60/100 ;
  219. m_nHalfBaseWidth = m_nRadiusPix/40 ;
  220. dTemp = m_nCXPix - m_nRadiusPix*sin(m_dLimitAngleRad) ;
  221. m_nLeftLimitXPix = ROUND(dTemp) ;
  222. dTemp = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad) ;
  223. m_nLeftLimitYPix = ROUND(dTemp) ;
  224. dTemp = m_nCXPix + m_nRadiusPix*sin(m_dLimitAngleRad) ;
  225. m_nRightLimitXPix = ROUND(dTemp) ;
  226. m_nRightLimitYPix = m_nLeftLimitYPix ;
  227. // determine the placement of the current value text
  228. m_rectValue.left = m_rectDraw.left+1 ;
  229. m_rectValue.top = m_rectDraw.top+1 ;
  230. m_rectValue.right = m_rectDraw.right-1 ;
  231. if (m_boolUseBitmaps)
  232. m_rectValue.bottom = m_rectDraw.top+m_nCYPix - m_nRadiusPix - 1 ;
  233. else
  234. m_rectValue.bottom = m_nCYPix - m_nRadiusPix - 1 ;
  235. // determine the placement of the minimum value
  236. m_rectMinValue.left = m_rectDraw.left+1 ;
  237. m_rectMinValue.top = m_nCYPix - m_nRectHeight*3/20 ;
  238. m_rectMinValue.right = m_nLeftLimitXPix + 3*(m_nCXPix-m_nLeftLimitXPix)/4 ;
  239. m_rectMinValue.bottom = m_nCYPix + m_nRectHeight/20 ;
  240. // determine the placement of the maximum value
  241. m_rectMaxValue.right = m_rectDraw.right-1 ;
  242. m_rectMaxValue.top = m_nCYPix - m_nRectHeight*3/20 ;
  243. m_rectMaxValue.left = m_nRightLimitXPix - 3*(m_nRightLimitXPix-m_nCXPix)/4 ;
  244. m_rectMaxValue.bottom = m_nCYPix + m_nRectHeight/20 ;
  245. // create a font based on these sizes
  246. nFontHeight = m_rectMaxValue.Height()*4/5 ;  // modify the fraction to adjust
  247. m_fontValue.DeleteObject() ;
  248. m_fontValue.CreateFont (nFontHeight, 0, 0, 0, 400,
  249. FALSE, FALSE, 0, ANSI_CHARSET,
  250. OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  251. DEFAULT_QUALITY, DEFAULT_PITCH|FF_SWISS, "Arial") ;
  252. // grab the font and set the text color
  253. oldFont = m_dcGrid.SelectObject(&m_fontValue) ;
  254. m_dcGrid.SetTextColor(m_colorGrid) ;
  255. m_nTextBaseSpacing = m_rectMinValue.Height()/4 ;
  256. // show the title
  257. m_dcGrid.SetTextAlign(TA_CENTER|TA_BOTTOM) ;
  258. m_dcGrid.TextOut ((m_rectDraw.left+m_rectDraw.right)/2,
  259.                  m_rectDraw.bottom-1, m_strTitle) ;
  260. // show the max and min (limit) values
  261. m_dcGrid.SetTextAlign(TA_CENTER|TA_BASELINE) ;
  262. tempString.Format("%.*f", m_nRangeDecimals, m_dMinScale) ;
  263. m_dcGrid.TextOut ((m_rectMinValue.left+m_rectMinValue.right)/2, 
  264.                  m_rectMinValue.bottom-m_nTextBaseSpacing, tempString) ;
  265. tempString.Format("%.*f", m_nRangeDecimals, m_dMaxScale) ;
  266. m_dcGrid.TextOut ((m_rectMaxValue.left+m_rectMaxValue.right)/2, 
  267.                  m_rectMaxValue.bottom-m_rectMaxValue.Height()/4, tempString) ;
  268. // restore the font
  269. m_dcGrid.SelectObject(oldFont) ;
  270. // create the pen and brush for drawing
  271. penSolid.DeleteObject() ;
  272. penSolid.CreatePen(PS_SOLID, 1, m_colorGrid) ;
  273. // grab the pen
  274. oldPen = m_dcGrid.SelectObject(&penSolid) ;
  275. // determine the bounding rectangle for the pie slice
  276. // and draw it
  277. nLeftBoundX = m_nCXPix - m_nRadiusPix ;
  278. nRightBoundX = m_nCXPix + m_nRadiusPix ;
  279. nLeftBoundY = m_nCYPix - m_nRadiusPix ;
  280. nRightBoundY = m_nCYPix + m_nRadiusPix ;
  281. m_dcGrid.Pie(nLeftBoundX, nLeftBoundY, nRightBoundX+1, nRightBoundY+1,
  282.    m_nRightLimitXPix, m_nRightLimitYPix, m_nLeftLimitXPix, m_nLeftLimitYPix) ;
  283. /*
  284. // center tick mark
  285. m_dcGrid.MoveTo (m_nCXPix, m_nCYPix-m_nRadiusPix) ;
  286. m_dcGrid.LineTo (m_nCXPix, m_nCYPix-46*m_nRadiusPix/50) ;
  287. // left tick mark
  288. dX = m_nCXPix - m_nRadiusPix*sin(m_dLimitAngleRad/2) ;
  289. dY = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad/2) ;
  290. m_dcGrid.MoveTo(ROUND(dX), ROUND(dY)) ;
  291. dX = m_nCXPix - 46*m_nRadiusPix*sin(m_dLimitAngleRad/2)/50 ;
  292. dY = m_nCYPix - 46*m_nRadiusPix*cos(m_dLimitAngleRad/2)/50 ;
  293. m_dcGrid.LineTo(ROUND(dX), ROUND(dY)) ;
  294. // right tick mark
  295. dX = m_nCXPix + m_nRadiusPix*sin(m_dLimitAngleRad/2) ;
  296. dY = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad/2) ;
  297. m_dcGrid.MoveTo(ROUND(dX), ROUND(dY)) ;
  298. dX = m_nCXPix + 46*m_nRadiusPix*sin(m_dLimitAngleRad/2)/50 ;
  299. dY = m_nCYPix - 46*m_nRadiusPix*cos(m_dLimitAngleRad/2)/50 ;
  300. m_dcGrid.LineTo(ROUND(dX), ROUND(dY)) ;
  301. */
  302. double RadpP = 2.0*m_dLimitAngleRad/10.0;
  303.     double Rad; 
  304.     for(int k = 0;k<10;k++)
  305. {
  306.         Rad = (3.14159 - 2.0*m_dLimitAngleRad)/2.0;
  307.         Rad += (k+1)*RadpP;
  308.  //       if(Rad > 3.14159/2)
  309.  // Rad -= 3.14159/2;
  310.         dX = m_nCXPix - m_nRadiusPix*cos(Rad);
  311.         dY = m_nCYPix - m_nRadiusPix*sin(Rad);
  312.         m_dcGrid.MoveTo(ROUND(dX), ROUND(dY));
  313.         dX = m_nCXPix - 45*m_nRadiusPix*cos(Rad)/50 ;
  314.     dY = m_nCYPix - 45*m_nRadiusPix*sin(Rad)/50 ;
  315.       m_dcGrid.LineTo(ROUND(dX), ROUND(dY)) ;
  316. }
  317. // draw circle at the bottom
  318. brushSolid.CreateSolidBrush (m_colorGrid) ;
  319. oldBrush = m_dcGrid.SelectObject(&brushSolid) ;
  320. m_dcGrid.Ellipse (m_nCXPix-m_nHalfBaseWidth, m_nCYPix-m_nHalfBaseWidth,
  321.                 m_nCXPix+m_nHalfBaseWidth+1, m_nCYPix+m_nHalfBaseWidth+1) ;
  322. m_dcGrid.SelectObject(oldBrush) ;
  323. m_dcGrid.SelectObject(oldPen) ;
  324. } // end DrawGrid
  325. ///////////////////////////////////
  326. void CAnalogMeter::DrawNeedle()
  327. {
  328. CPoint pPoints[6] ;
  329. CString tempString ;
  330. CFont *oldFont ;
  331. CPen *oldPen, solidPen ;
  332. CBrush *oldBrush, solidBrush ;
  333. double dAngleRad, dX, dY ;
  334. double dCosAngle, dSinAngle ;
  335. if (!m_dcNeedle.GetSafeHdc())
  336. return ;
  337. if (m_boolUseBitmaps)
  338. {
  339. oldPen = m_dcNeedle.SelectObject(&m_penErase) ;
  340. oldBrush = m_dcNeedle.SelectObject(&m_brushErase) ;
  341. m_dcNeedle.Rectangle(m_rectDraw) ;
  342. m_dcNeedle.SelectObject(oldBrush) ;
  343. m_dcNeedle.SelectObject(oldPen) ;
  344. }
  345. oldFont = m_dcNeedle.SelectObject(&m_fontValue) ;
  346. m_dcNeedle.SetTextAlign(TA_CENTER|TA_BASELINE) ;
  347. m_dcNeedle.SetTextColor(m_colorValue) ;
  348. tempString.Format("%.*f ", m_nValueDecimals, m_dPercent) ;
  349. //tempString.Format("%f ",  m_dPercent) ;
  350. m_dcNeedle.TextOut ((m_rectValue.right+m_rectValue.left)/2, 
  351.                  m_rectValue.bottom-m_nTextBaseSpacing, tempString) ;
  352. m_dcNeedle.SelectObject(oldFont) ;
  353. dAngleRad = m_dNeedlePos;
  354. //(m_dNeedlePos - m_dMinScale)*m_dRadiansPerValue 
  355. //         - m_dLimitAngleRad ;
  356. dAngleRad = max(dAngleRad, -m_dLimitAngleRad) ;
  357. dAngleRad = min(dAngleRad, m_dLimitAngleRad) ;
  358. dCosAngle = cos(dAngleRad) ;
  359. dSinAngle = sin(dAngleRad) ;
  360. // tip
  361. dX = m_nCXPix + m_nRadiusPix*dSinAngle ;
  362. dY = m_nCYPix - m_nRadiusPix*dCosAngle ;
  363. pPoints[0].x = ROUND(dX) ;
  364. pPoints[0].y = ROUND(dY) ;
  365. // left base
  366. dX = m_nCXPix - m_nHalfBaseWidth*dCosAngle ;
  367. dY = m_nCYPix - m_nHalfBaseWidth*dSinAngle ;
  368. pPoints[1].x = ROUND(dX) ;
  369. pPoints[1].y = ROUND(dY) ;
  370. // right base
  371. pPoints[2].x = m_nCXPix + (m_nCXPix-pPoints[1].x) ;
  372. pPoints[2].y = m_nCYPix + (m_nCYPix-pPoints[1].y) ;
  373. // tip
  374. pPoints[3].x = pPoints[0].x ;
  375. pPoints[3].y = pPoints[0].y ;
  376. solidPen.CreatePen (PS_SOLID, 0, m_colorNeedle) ;
  377. solidBrush.CreateSolidBrush (m_colorNeedle) ;
  378. oldPen = m_dcNeedle.SelectObject(&solidPen) ;
  379. oldBrush = m_dcNeedle.SelectObject(&solidBrush) ;
  380. // draw the needle pointer
  381. m_dcNeedle.Polygon(pPoints, 4) ;
  382. m_dcNeedle.SelectObject(oldPen) ;
  383. m_dcNeedle.SelectObject(oldBrush) ;
  384. // draw the circle at the bottom of the needle
  385. m_dcNeedle.Ellipse (m_nCXPix-m_nHalfBaseWidth, m_nCYPix-m_nHalfBaseWidth,
  386.                   m_nCXPix+m_nHalfBaseWidth+1, m_nCYPix+m_nHalfBaseWidth+1) ;
  387. m_dcNeedle.SelectObject(oldPen) ;
  388. m_dcNeedle.SelectObject(oldBrush) ;
  389. } // end DrawNeedle
  390. //////////////////////////////////////////////////////
  391. void CAnalogMeter::SetRange(double dMin, double dMax)
  392. {
  393. // Note, this only changes the plotting range. 
  394. // It does NOT force the re-drawing of the meter.
  395. // The owner must explicitly call the ShowMeter function
  396. // to get the new range values to display.
  397. m_dMinScale = dMin ;
  398. m_dMaxScale = dMax ;
  399. m_boolForceRedraw = TRUE ;
  400. }
  401. //////////////////////////////////////////////////////
  402. void CAnalogMeter::SetRangeDecimals(int nRangeDecimals)
  403. {
  404. m_nRangeDecimals = nRangeDecimals;
  405. m_boolForceRedraw = TRUE ;
  406. }
  407. //////////////////////////////////////////////////////
  408. void CAnalogMeter::SetValueDecimals(int nValueDecimals)
  409. {
  410. m_nValueDecimals = nValueDecimals;
  411. m_boolForceRedraw = TRUE ;
  412. }
  413. //////////////////////////////////////////////////////
  414. void CAnalogMeter::SetTitle(CString strTitle)
  415. {
  416. m_strTitle = strTitle ;
  417.     m_boolForceRedraw = TRUE ;
  418. }