// OScopeCtrl.cpp : implementation file//
#include "stdafx.h"
#include "math.h"
#include "OScopeCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__ ;
#endif
/////////////////////////////////////////////////////////////////////////////
// COScopeCtrl
COScopeCtrl::COScopeCtrl()
{
// since plotting is based on a LineTo for each new point
// we need a starting point (i.e. a "previous" point)
// use 0.0 as the default first point.
// these are public member variables, and can be changed outside
// (after construction). Therefore m_perviousPosition could be set to
// a more appropriate value prior to the first call to SetPosition.
m_dPreviousPosition = 0.0 ;
// public variable for the number of decimal places on the y axis
m_nYDecimals = 3 ;
// set some initial values for the scaling until "SetRange" is called.
// these are protected varaibles and must be set with SetRange
// in order to ensure that m_dRange is updated accordingly
m_dLowerLimit = -10.0 ;
m_dUpperLimit = 10.0 ;
m_dRange = m_dUpperLimit - m_dLowerLimit ; // protected member variable
// m_nShiftPixels determines how much the plot shifts (in terms of pixels)
// with the addition of a new data point
m_nShiftPixels = 4 ;
m_nHalfShiftPixels = m_nShiftPixels/2 ; // protected
m_nPlotShiftPixels = m_nShiftPixels + m_nHalfShiftPixels ; // protected
// background, grid and data colors
// these are public variables and can be set directly
m_crBackColor = RGB( 0, 0, 0) ; // see also SetBackgroundColor
m_crGridColor = RGB( 0, 255, 255) ; // see also SetGridColor
m_crPlotColor = RGB(255, 255, 255) ; // see also SetPlotColor
// protected variables
m_penPlot.CreatePen(PS_SOLID, 0, m_crPlotColor) ;
m_brushBack.CreateSolidBrush(m_crBackColor) ;
// public member variables, can be set directly
m_strXUnitsString.Format("Samples") ; // can also be set with SetXUnits
m_strYUnitsString.Format("Y units") ; // can also be set with SetYUnits
// protected bitmaps to restore the memory DC's
m_pbitmapOldGrid = NULL ;
m_pbitmapOldPlot = NULL ;
} // COScopeCtrl
/////////////////////////////////////////////////////////////////////////////
COScopeCtrl::~COScopeCtrl()
{
// just to be picky restore the bitmaps for the two memory dc's
// (these dc's are being destroyed so there shouldn't be any leaks)
if (m_pbitmapOldGrid != NULL)
m_dcGrid.SelectObject(m_pbitmapOldGrid) ;
if (m_pbitmapOldPlot != NULL)
m_dcPlot.SelectObject(m_pbitmapOldPlot) ;
} // ~COScopeCtrl
BEGIN_MESSAGE_MAP(COScopeCtrl, CWnd)
//{{AFX_MSG_MAP(COScopeCtrl)
ON_WM_PAINT()
ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COScopeCtrl message handlers
/////////////////////////////////////////////////////////////////////////////
BOOL COScopeCtrl::Create(DWORD dwStyle, const RECT& rect,
CWnd* pParentWnd, UINT nID)
{
BOOL result ;
static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW) ;
result = CWnd::CreateEx(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE,
className, NULL, dwStyle,
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)nID) ;
if (result != 0)
InvalidateCtrl() ;
return result ;
} // Create
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetRange(double dLower, double dUpper, int nDecimalPlaces)
{
ASSERT(dUpper > dLower) ;
m_dLowerLimit = dLower ;
m_dUpperLimit = dUpper ;
m_nYDecimals = nDecimalPlaces ;
m_dRange = m_dUpperLimit - m_dLowerLimit ;
m_dVerticalFactor = (double)m_nPlotHeight / m_dRange ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetRange
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetXUnits(CString string)
{
m_strXUnitsString = string ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetXUnits
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetYUnits(CString string)
{
m_strYUnitsString = string ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetYUnits
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetGridColor(COLORREF color)
{
m_crGridColor = color ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetGridColor
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetPlotColor(COLORREF color)
{
m_crPlotColor = color ;
m_penPlot.DeleteObject() ;
m_penPlot.CreatePen(PS_SOLID, 0, m_crPlotColor) ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetPlotColor
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::SetBackgroundColor(COLORREF color)
{
m_crBackColor = color ;
m_brushBack.DeleteObject() ;
m_brushBack.CreateSolidBrush(m_crBackColor) ;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl() ;
} // SetBackgroundColor
/////////////////////////////////////////////////////////////////////////////
void COScopeCtrl::InvalidateCtrl()
{
// There is a lot of drawing going on here - particularly in terms of
// drawing the grid. Don't panic, this is all being drawn (only once)
// to a bitmap. The result is then BitBlt'd to the control whenever needed.
int i ;
int nCharacters ;
int nTopGridPix, nMidGridPix, nBottomGridPix ;
CPen *oldPen ;
CPen solidPen(PS_SOLID, 0, m_crGridColor) ;
CFont axisFont, yUnitFont, *oldFont ;
CString strTemp ;
// in case we haven't established the memory dc's
CClientDC dc(this) ;
// if we don't have one yet, set up a memory dc for the grid
if (m_dcGrid.GetSafeHdc() == NULL)
{
m_dcGrid.CreateCompatibleDC(&dc) ;
m_bitmapGrid.CreateCompatibleBitmap(&dc, m_nClientWidth, m_nClientHeight) ;
m_pbitmapOldGrid = m_dcGrid.SelectObject(&m_bitmapGrid) ;
}
m_dcGrid.SetBkColor (m_crBackColor) ;
// fill the grid background
m_dcGrid.FillRect(m_rectClient, &m_brushBack) ;
// draw the plot rectangle:
// determine how wide the y axis scaling values are
nCharacters = abs((int)log10(fabs(m_dUpperLimit))) ;
nCharacters = max(nCharacters, abs((int)log10(fabs(m_dLowerLimit)))) ;
// add the units digit, decimal point and a minus sign, and an extra space
// as well as the number of decimal places to display
nCharacters = nCharacters + 4 + m_nYDecimals ;
// adjust the plot rectangle dimensions
// assume 6 pixels per character (this may need to be adjusted)
m_rectPlot.left = m_rectClient.left + 6*(nCharacters) ;
m_nPlotWidth = m_rectPlot.Width() ;
// draw the plot rectangle
oldPen = m_dcGrid.SelectObject (&solidPen) ;
m_dcGrid.MoveTo (m_rectPlot.left, m_rectPlot.top) ;
m_dcGrid.LineTo (m_rectPlot.right+1, m_rectPlot.top) ;
m_dcGrid.LineTo (m_rectPlot.right+1, m_rectPlot.bottom+1) ;
m_dcGrid.LineTo (m_rectPlot.left, m_rectPlot.bottom+1) ;
m_dcGrid.LineTo (m_rectPlot.left, m_rectPlot.top) ;
m_dcGrid.SelectObject (oldPen) ;
// draw the dotted lines,
// use SetPixel instead of a dotted pen - this allows for a
// finer dotted line and a more "technical" look
nMidGridPix = (m_rectPlot.top + m_rectPlot.bottom)/2 ;
nTopGridPix = nMidGridPi