www.pudn.com > SNMP·¶ÀýÔ´´úÂë.zip > ANALOGMETER.CPP
// AnalogMeter.cpp : implementation file // #include "stdafx.h" #include#include "AnalogMeter.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define ROUND(x) (int)((x) + 0.5 - (double)((x) < 0)) ///////////////////////////////////////////////////////////////////////////// // CAnalogMeter IMPLEMENT_DYNCREATE(CAnalogMeter, CCmdTarget) CAnalogMeter::CAnalogMeter() { m_dPI = 4.0*atan(1.0) ; // for trig calculations // initialized rectangle locations, will be modified on first drawing m_rectDraw = CRect(0, 0, 0, 0) ; m_nRectWidth = 0; m_nRectHeight = 0; // draw the whole thing the first time m_boolForceRedraw = TRUE ; m_dRadiansPerValue = 0.0 ; // will be modified on first drawing // FALSE if we are printing m_boolUseBitmaps = TRUE ; // default titles, scaling and needle position m_dMinScale = -10.0 ; m_dMaxScale = 10.0 ; m_dNeedlePos = 0.0 ; m_strTitle.Format("Volts") ; // for numerical values m_nRangeDecimals = 1 ; m_nValueDecimals = 3 ; // grid color m_colorGrid = RGB(128, 128, 128) ; // current numerical value color m_colorValue = RGB(0, 0, 0) ; // needle color m_colorNeedle = RGB(255, 0, 0) ; // background color brushes (for erasing) m_brushErase.CreateSolidBrush(RGB(255, 255, 255)) ; m_penErase.CreatePen(PS_SOLID, 0, RGB(255, 255, 255)) ; } CAnalogMeter::~CAnalogMeter() { if(m_dcGrid.m_hDC) { m_dcGrid.SelectObject(m_pbitmapOldGrid) ; m_dcGrid.DeleteDC() ; } if(m_dcNeedle.m_hDC) { m_dcNeedle.SelectObject(m_pbitmapOldNeedle) ; m_dcNeedle.DeleteDC() ; } if(m_bitmapGrid.m_hObject) m_bitmapGrid.DeleteObject() ; if(m_bitmapNeedle.m_hObject) m_bitmapNeedle.DeleteObject() ; } BEGIN_MESSAGE_MAP(CAnalogMeter, CCmdTarget) //{{AFX_MSG_MAP(CAnalogMeter) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAnalogMeter message handlers void CAnalogMeter::ShowMeter(CDC * pDC, CRect rectBorder) { // check for a new meter or a resize of the old one. // (if the rectangles have changed, then redraw from scratch). // If we are printing, always draw from scratch without bitmaps. if (m_rectOwner != rectBorder) m_boolForceRedraw = TRUE ; if (m_boolForceRedraw || (pDC->IsPrinting())) { m_boolForceRedraw = FALSE ; // first store the rectangle for the owner // and determine the rectangle to draw to m_rectOwner = rectBorder ; if (pDC->IsPrinting()) { m_boolUseBitmaps = FALSE ; m_rectDraw = m_rectOwner ; // draw directly to the owner } else { m_boolUseBitmaps = TRUE ; m_rectDraw.left = 0 ; // draw to a bitmap rectangle m_rectDraw.top = 0 ; m_rectDraw.right = rectBorder.Width() ; m_rectDraw.bottom = rectBorder.Height() ; } m_nRectWidth = m_rectDraw.Width() ; m_nRectHeight = m_rectDraw.Height() ; // if we already have a memory dc, destroy it // (this occurs for a re-size of the meter) if (m_dcGrid.GetSafeHdc()) { m_dcGrid.SelectObject(m_pbitmapOldGrid) ; m_dcGrid.DeleteDC() ; m_dcNeedle.SelectObject(m_pbitmapOldNeedle) ; m_dcNeedle.DeleteDC() ; m_bitmapGrid.DeleteObject() ; m_bitmapNeedle.DeleteObject() ; } if (m_boolUseBitmaps) { // create a memory based dc for drawing the grid m_dcGrid.CreateCompatibleDC(pDC) ; m_bitmapGrid.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ; m_pbitmapOldGrid = m_dcGrid.SelectObject(&m_bitmapGrid) ; // create a memory based dc for drawing the needle m_dcNeedle.CreateCompatibleDC(pDC) ; m_bitmapNeedle.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ; m_pbitmapOldNeedle = m_dcNeedle.SelectObject(&m_bitmapNeedle) ; } else // no bitmaps, draw to the destination { // use the destination dc for the grid m_dcGrid.m_hDC = pDC->m_hDC ; m_dcGrid.m_hAttribDC = pDC->m_hAttribDC ; // use the destination dc for the grid m_dcNeedle.m_hDC = pDC->m_hDC ; m_dcNeedle.m_hAttribDC = pDC->m_hAttribDC ; } // draw the grid in the to the "grid dc" DrawGrid () ; // draw the needle in the "needle dc" DrawNeedle () ; } // display the new image, combining the needle with the grid if (m_boolUseBitmaps) ShowMeterImage(pDC); } // end ShowMeter void CAnalogMeter::ShowMeterImage(CDC *pDC) { CDC memDC ; CBitmap memBitmap ; CBitmap* oldBitmap ; // bitmap originally found in CMemDC // this function is only used when the needle and grid // have been drawn to bitmaps and they need to be combined // and sent to the destination if (!m_boolUseBitmaps) return ; // to avoid flicker, establish a memory dc, draw to it // and then BitBlt it to the destination "pDC" memDC.CreateCompatibleDC(pDC) ; memBitmap.CreateCompatibleBitmap(pDC, m_nRectWidth, m_nRectHeight) ; oldBitmap = (CBitmap *)memDC.SelectObject(&memBitmap) ; // make sure we have the bitmaps if (!m_dcGrid.GetSafeHdc()) return ; if (!m_dcNeedle.GetSafeHdc()) return ; if (memDC.GetSafeHdc() != NULL) { // draw the inverted grid memDC.BitBlt(0, 0, m_nRectWidth, m_nRectHeight, &m_dcGrid, 0, 0, NOTSRCCOPY) ; // merge the needle image with the grid memDC.BitBlt(0, 0, m_nRectWidth, m_nRectHeight, &m_dcNeedle, 0, 0, SRCINVERT) ; // copy the resulting bitmap to the destination pDC->BitBlt(m_rectOwner.left, m_rectOwner.top, m_nRectWidth, m_nRectHeight, &memDC, 0, 0, SRCCOPY) ; } memDC.SelectObject(oldBitmap) ; } // end ShowMeterImage ////////////////////////////////////////////////////// void CAnalogMeter::UpdateNeedle(CDC *pDC, double dPos) { // do not support updates if we are not working with // bitmaps images if (!m_boolUseBitmaps) return ; // must have created the grid if we are going to // update the needle (the needle locations are // calculateed based on the grid) if (!m_dcGrid.GetSafeHdc()) return ; // if the needle hasn't changed, don't bother updating if (m_dNeedlePos == dPos) return ; // store the position in the member variable // for availability elsewhere m_dNeedlePos = dPos ; // draw the new needle image DrawNeedle () ; // combine the needle with the grid and display the result ShowMeterImage (pDC) ; } // end UpdateNeedle ////////////////////////////////////////// void CAnalogMeter::DrawGrid () { int nFontHeight ; int nLeftBoundX, nRightBoundX, nLeftBoundY, nRightBoundY ; double dLimitAngleDeg = 45.0 ; // this specifies the width of the pie slice double dX, dY, dTemp ; CPen penSolid, *oldPen ; CBrush brushSolid, *oldBrush ; CFont *oldFont ; CString tempString ; // draw the boundary rectangle and // fill the entire area with the background color penSolid.CreatePen(PS_SOLID, 0, RGB(0, 0, 0)) ; oldPen = m_dcGrid.SelectObject(&penSolid) ; oldBrush = m_dcGrid.SelectObject(&m_brushErase) ; m_dcGrid.Rectangle(m_rectDraw) ; m_dcGrid.SelectObject(oldBrush) ; m_dcGrid.SelectObject(oldPen) ; // determine the angular scaling m_dLimitAngleRad = dLimitAngleDeg*m_dPI/180.0 ; m_dRadiansPerValue = (2.0*m_dLimitAngleRad)/(m_dMaxScale-m_dMinScale) ; // determine the center point m_nCXPix = (m_rectDraw.left+m_rectDraw.right)/2 ; m_nCYPix = m_rectDraw.bottom - m_nRectHeight/5 ; // determine the size and location of the meter "pie" m_nRadiusPix = m_nRectWidth*60/100 ; m_nHalfBaseWidth = m_nRadiusPix/40 ; dTemp = m_nCXPix - m_nRadiusPix*sin(m_dLimitAngleRad) ; m_nLeftLimitXPix = ROUND(dTemp) ; dTemp = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad) ; m_nLeftLimitYPix = ROUND(dTemp) ; dTemp = m_nCXPix + m_nRadiusPix*sin(m_dLimitAngleRad) ; m_nRightLimitXPix = ROUND(dTemp) ; m_nRightLimitYPix = m_nLeftLimitYPix ; // determine the placement of the current value text m_rectValue.left = m_rectDraw.left+1 ; m_rectValue.top = m_rectDraw.top+1 ; m_rectValue.right = m_rectDraw.right-1 ; if (m_boolUseBitmaps) m_rectValue.bottom = m_rectDraw.top+m_nCYPix - m_nRadiusPix - 1 ; else m_rectValue.bottom = m_nCYPix - m_nRadiusPix - 1 ; // determine the placement of the minimum value m_rectMinValue.left = m_rectDraw.left+1 ; m_rectMinValue.top = m_nCYPix - m_nRectHeight*3/20 ; m_rectMinValue.right = m_nLeftLimitXPix + 3*(m_nCXPix-m_nLeftLimitXPix)/4 ; m_rectMinValue.bottom = m_nCYPix + m_nRectHeight/20 ; // determine the placement of the maximum value m_rectMaxValue.right = m_rectDraw.right-1 ; m_rectMaxValue.top = m_nCYPix - m_nRectHeight*3/20 ; m_rectMaxValue.left = m_nRightLimitXPix - 3*(m_nRightLimitXPix-m_nCXPix)/4 ; m_rectMaxValue.bottom = m_nCYPix + m_nRectHeight/20 ; // create a font based on these sizes nFontHeight = m_rectMaxValue.Height()*4/5 ; // modify the fraction to adjust m_fontValue.DeleteObject() ; m_fontValue.CreateFont (nFontHeight, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_SWISS, "Arial") ; // grab the font and set the text color oldFont = m_dcGrid.SelectObject(&m_fontValue) ; m_dcGrid.SetTextColor(m_colorGrid) ; m_nTextBaseSpacing = m_rectMinValue.Height()/4 ; // show the title m_dcGrid.SetTextAlign(TA_CENTER|TA_BOTTOM) ; m_dcGrid.TextOut ((m_rectDraw.left+m_rectDraw.right)/2, m_rectDraw.bottom-1, m_strTitle) ; // show the max and min (limit) values m_dcGrid.SetTextAlign(TA_CENTER|TA_BASELINE) ; tempString.Format("%.*f", m_nRangeDecimals, m_dMinScale) ; m_dcGrid.TextOut ((m_rectMinValue.left+m_rectMinValue.right)/2, m_rectMinValue.bottom-m_nTextBaseSpacing, tempString) ; tempString.Format("%.*f", m_nRangeDecimals, m_dMaxScale) ; m_dcGrid.TextOut ((m_rectMaxValue.left+m_rectMaxValue.right)/2, m_rectMaxValue.bottom-m_rectMaxValue.Height()/4, tempString) ; // restore the font m_dcGrid.SelectObject(oldFont) ; // create the pen and brush for drawing penSolid.DeleteObject() ; penSolid.CreatePen(PS_SOLID, 0, m_colorGrid) ; // grab the pen oldPen = m_dcGrid.SelectObject(&penSolid) ; // determine the bounding rectangle for the pie slice // and draw it nLeftBoundX = m_nCXPix - m_nRadiusPix ; nRightBoundX = m_nCXPix + m_nRadiusPix ; nLeftBoundY = m_nCYPix - m_nRadiusPix ; nRightBoundY = m_nCYPix + m_nRadiusPix ; m_dcGrid.Pie(nLeftBoundX, nLeftBoundY, nRightBoundX+1, nRightBoundY+1, m_nRightLimitXPix, m_nRightLimitYPix, m_nLeftLimitXPix, m_nLeftLimitYPix) ; // center tick mark m_dcGrid.MoveTo (m_nCXPix, m_nCYPix-m_nRadiusPix) ; m_dcGrid.LineTo (m_nCXPix, m_nCYPix-46*m_nRadiusPix/50) ; // left tick mark dX = m_nCXPix - m_nRadiusPix*sin(m_dLimitAngleRad/2) ; dY = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad/2) ; m_dcGrid.MoveTo(ROUND(dX), ROUND(dY)) ; dX = m_nCXPix - 46*m_nRadiusPix*sin(m_dLimitAngleRad/2)/50 ; dY = m_nCYPix - 46*m_nRadiusPix*cos(m_dLimitAngleRad/2)/50 ; m_dcGrid.LineTo(ROUND(dX), ROUND(dY)) ; // right tick mark dX = m_nCXPix + m_nRadiusPix*sin(m_dLimitAngleRad/2) ; dY = m_nCYPix - m_nRadiusPix*cos(m_dLimitAngleRad/2) ; m_dcGrid.MoveTo(ROUND(dX), ROUND(dY)) ; dX = m_nCXPix + 46*m_nRadiusPix*sin(m_dLimitAngleRad/2)/50 ; dY = m_nCYPix - 46*m_nRadiusPix*cos(m_dLimitAngleRad/2)/50 ; m_dcGrid.LineTo(ROUND(dX), ROUND(dY)) ; // draw circle at the bottom brushSolid.CreateSolidBrush (m_colorGrid) ; oldBrush = m_dcGrid.SelectObject(&brushSolid) ; m_dcGrid.Ellipse (m_nCXPix-m_nHalfBaseWidth, m_nCYPix-m_nHalfBaseWidth, m_nCXPix+m_nHalfBaseWidth+1, m_nCYPix+m_nHalfBaseWidth+1) ; m_dcGrid.SelectObject(oldBrush) ; m_dcGrid.SelectObject(oldPen) ; } // end DrawGrid /////////////////////////////////// void CAnalogMeter::DrawNeedle() { CPoint pPoints[6] ; CString tempString ; CFont *oldFont ; CPen *oldPen, solidPen ; CBrush *oldBrush, solidBrush ; double dAngleRad, dX, dY ; double dCosAngle, dSinAngle ; if (!m_dcNeedle.GetSafeHdc()) return ; if (m_boolUseBitmaps) { oldPen = m_dcNeedle.SelectObject(&m_penErase) ; oldBrush = m_dcNeedle.SelectObject(&m_brushErase) ; m_dcNeedle.Rectangle(m_rectDraw) ; m_dcNeedle.SelectObject(oldBrush) ; m_dcNeedle.SelectObject(oldPen) ; } oldFont = m_dcNeedle.SelectObject(&m_fontValue) ; m_dcNeedle.SetTextAlign(TA_CENTER|TA_BASELINE) ; m_dcNeedle.SetTextColor(m_colorValue) ; tempString.Format("%.*f", m_nValueDecimals, m_dNeedlePos) ; m_dcNeedle.TextOut ((m_rectValue.right+m_rectValue.left)/2, m_rectValue.bottom-m_nTextBaseSpacing, tempString) ; m_dcNeedle.SelectObject(oldFont) ; dAngleRad = (m_dNeedlePos - m_dMinScale)*m_dRadiansPerValue - m_dLimitAngleRad ; dAngleRad = max(dAngleRad, -m_dLimitAngleRad) ; dAngleRad = min(dAngleRad, m_dLimitAngleRad) ; dCosAngle = cos(dAngleRad) ; dSinAngle = sin(dAngleRad) ; // tip dX = m_nCXPix + m_nRadiusPix*dSinAngle ; dY = m_nCYPix - m_nRadiusPix*dCosAngle ; pPoints[0].x = ROUND(dX) ; pPoints[0].y = ROUND(dY) ; // left base dX = m_nCXPix - m_nHalfBaseWidth*dCosAngle ; dY = m_nCYPix - m_nHalfBaseWidth*dSinAngle ; pPoints[1].x = ROUND(dX) ; pPoints[1].y = ROUND(dY) ; // right base pPoints[2].x = m_nCXPix + (m_nCXPix-pPoints[1].x) ; pPoints[2].y = m_nCYPix + (m_nCYPix-pPoints[1].y) ; // tip pPoints[3].x = pPoints[0].x ; pPoints[3].y = pPoints[0].y ; solidPen.CreatePen (PS_SOLID, 0, m_colorNeedle) ; solidBrush.CreateSolidBrush (m_colorNeedle) ; oldPen = m_dcNeedle.SelectObject(&solidPen) ; oldBrush = m_dcNeedle.SelectObject(&solidBrush) ; // draw the needle pointer m_dcNeedle.Polygon(pPoints, 4) ; m_dcNeedle.SelectObject(oldPen) ; m_dcNeedle.SelectObject(oldBrush) ; // draw the circle at the bottom of the needle m_dcNeedle.Ellipse (m_nCXPix-m_nHalfBaseWidth, m_nCYPix-m_nHalfBaseWidth, m_nCXPix+m_nHalfBaseWidth+1, m_nCYPix+m_nHalfBaseWidth+1) ; m_dcNeedle.SelectObject(oldPen) ; m_dcNeedle.SelectObject(oldBrush) ; } // end DrawNeedle ////////////////////////////////////////////////////// void CAnalogMeter::SetRange(double dMin, double dMax) { // Note, this only changes the plotting range. // It does NOT force the re-drawing of the meter. // The owner must explicitly call the ShowMeter function // to get the new range values to display. m_dMinScale = dMin ; m_dMaxScale = dMax ; m_boolForceRedraw = TRUE ; } ////////////////////////////////////////////////////// void CAnalogMeter::SetRangeDecimals(int nRangeDecimals) { m_nRangeDecimals = nRangeDecimals; m_boolForceRedraw = TRUE ; } ////////////////////////////////////////////////////// void CAnalogMeter::SetValueDecimals(int nValueDecimals) { m_nValueDecimals = nValueDecimals; m_boolForceRedraw = TRUE ; } ////////////////////////////////////////////////////// void CAnalogMeter::SetTitle(CString strTitle) { m_strTitle = strTitle ; m_boolForceRedraw = TRUE ; }