📄 oscopectrl.cpp
字号:
// 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(int NTrends)
{
int i;
COLORREF PresetColor[16] =
{
RGB(0xFF, 0x00, 0x00),
RGB(0xFF, 0xC0, 0xC0),
RGB(0xFF, 0xFF, 0x00),
RGB(0xFF, 0xA0, 0x00),
RGB(0xA0, 0x60, 0x00),
RGB(0x00, 0xFF, 0x00),
RGB(0x00, 0xA0, 0x00),
RGB(0x00, 0x00, 0xFF),
RGB(0x00, 0xA0, 0xFF),
RGB(0x00, 0xFF, 0xFF),
RGB(0x00, 0xA0, 0xA0),
RGB(0xC0, 0xC0, 0xFF),
RGB(0xFF, 0x00, 0xFF),
RGB(0xA0, 0x00, 0xA0),
RGB(0xFF, 0xFF, 0xFF),
RGB(0x80, 0x80, 0x80)
};
// 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).
// G.Hayduk: NTrends is the number of trends that will be drawn on
// the plot. First 15 plots have predefined colors, but others will
// be drawn with white, unless you call SetPlotColor
m_PlotData = new PlotData_t[NTrends];
m_NTrends = NTrends;
for(i = 0; i < m_NTrends; i++)
{
if(i < 15)
m_PlotData[i].crPlotColor = PresetColor[i]; // see also SetPlotColor
else
m_PlotData[i].crPlotColor = RGB(255, 255, 255); // see also SetPlotColor
m_PlotData[i].penPlot.CreatePen(PS_SOLID, 0, m_PlotData[i].crPlotColor);
m_PlotData[i].dPreviousPosition = 0.0;
m_PlotData[i].nPrevY = -1;
m_PlotData[i].dLowerLimit = -10.0;
m_PlotData[i].dUpperLimit = 10.0;
m_PlotData[i].dRange = m_PlotData[i].dUpperLimit -
m_PlotData[i].dLowerLimit; // protected member variable
m_PlotData[i].lstPoints.AddTail(0.0);
}
// public variable for the number of decimal places on the y axis
// G.Hayduk: I've deleted the possibility of changing this parameter
// in SetRange, so change it after constructing the plot
m_nYDecimals = 1;
// 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_nShiftPixels determines how much the plot shifts (in terms of pixels)
// with the addition of a new data point
drawBars = false;
autofitYscale = false;
m_nShiftPixels = 1;
m_nTrendPoints = 0;
m_nMaxPointCnt = 1024;
CustShift.m_nPointsToDo = 0;
// G.Hayduk: actually I needed an OScopeCtrl to draw specific number of
// data samples and stretch them on the plot ctrl. Now, OScopeCtrl has
// two modes of operation: fixed Shift (when m_nTrendPoints=0,
// m_nShiftPixels is in use), or fixed number of Points in the plot width
// (when m_nTrendPoints>0)
// When m_nTrendPoints>0, CustShift structure is in use
// 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
// protected variables
m_brushBack.CreateSolidBrush(m_crBackColor);
// public member variables, can be set directly
m_str.XUnits.Format("Samples"); // can also be set with SetXUnits
m_str.YUnits.Format("Y units"); // can also be set with SetYUnits
// protected bitmaps to restore the memory DC's
m_pbitmapOldGrid = NULL;
m_pbitmapOldPlot = NULL;
// G.Hayduk: configurable number of grids init
// you are free to change those between contructing the object
// and calling Create
m_nXGrids = 6;
m_nYGrids = 5;
m_nTrendPoints = -1;
m_bDoUpdate = true;
m_nRedrawTimer = 0;
} // 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);
delete[] m_PlotData;
// G.Hayduk: If anyone notices that I'm not freeing or deleting
// something, please let me know: hayduk@hello.to
} // ~COScopeCtrl
BEGIN_MESSAGE_MAP(COScopeCtrl, CWnd)
//{{AFX_MSG_MAP(COScopeCtrl)
ON_WM_PAINT()
ON_WM_SIZE()
//}}AFX_MSG_MAP
ON_WM_TIMER()
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 iTrend)
{
ASSERT(dUpper > dLower);
m_PlotData[iTrend].dLowerLimit = dLower;
m_PlotData[iTrend].dUpperLimit = dUpper;
m_PlotData[iTrend].dRange = m_PlotData[iTrend].dUpperLimit - m_PlotData[iTrend].dLowerLimit;
m_PlotData[iTrend].dVerticalFactor = (double)m_nPlotHeight / m_PlotData[iTrend].dRange;
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl();
} // SetRange
void COScopeCtrl::SetRanges(double dLower, double dUpper)
{
int iTrend;
ASSERT(dUpper > dLower);
for(iTrend = 0; iTrend < m_NTrends; iTrend ++)
{
m_PlotData[iTrend].dLowerLimit = dLower;
m_PlotData[iTrend].dUpperLimit = dUpper;
m_PlotData[iTrend].dRange = m_PlotData[iTrend].dUpperLimit - m_PlotData[iTrend].dLowerLimit;
m_PlotData[iTrend].dVerticalFactor = (double)m_nPlotHeight / m_PlotData[iTrend].dRange;
}
// clear out the existing garbage, re-start with a clean plot
InvalidateCtrl();
} // SetRange
/////////////////////////////////////////////////////////////////////////////
// G.Hayduk: Apart from setting title of axis, now you can optionally set
// the limits strings
// (string which will be placed on the left and right of axis)
void COScopeCtrl::SetXUnits(CString string, CString XMin, CString XMax)
{
m_str.XUnits = string;
m_str.XMin = XMin;
m_str.XMax = XMax;
InvalidateCtrl(false);
} // SetXUnits
/////////////////////////////////////////////////////////////////////////////
// G.Hayduk: Apart from setting title of axis, now you can optionally set
// the limits strings
// (string which will be placed on the bottom and top of axis)
void COScopeCtrl::SetYUnits(CString string, CString YMin, CString YMax)
{
m_str.YUnits = string;
m_str.YMin = YMin;
m_str.YMax = YMax;
// 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, int iTrend)
{
m_PlotData[iTrend].crPlotColor = color;
m_PlotData[iTrend].penPlot.DeleteObject();
m_PlotData[iTrend].penPlot.CreatePen(PS_SOLID, 0, m_PlotData[iTrend].crPlotColor);
// clear out the existing garbage, re-start with a clean plot
// InvalidateCtrl() ;
} // SetPlotColor
COLORREF COScopeCtrl::GetPlotColor(int iTrend)
{
return m_PlotData[iTrend].crPlotColor;
} // GetPlotColor
/////////////////////////////////////////////////////////////////////////////
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(bool deleteGraph)
{
// 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, j, GridPos;
int nCharacters;
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.DeleteObject();
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_PlotData[0].dUpperLimit)));
nCharacters = max(nCharacters, abs((int)log10(fabs(m_PlotData[0].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*7+4;//(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
// G.Hayduk: added configurable number of grids
for(j = 1; j < (m_nYGrids + 1); j++)
{
GridPos = m_rectPlot.Height()*j/ (m_nYGrids + 1) + m_rectPlot.top;
for(i = m_rectPlot.left; i < m_rectPlot.right; i += 4)
m_dcGrid.SetPixel(i, GridPos, m_crGridColor);
}
/*
for(j = 1; j < (m_nXGrids + 1); j++)
{
GridPos = m_rectPlot.Width()*j/ (m_nXGrids + 1) + m_rectPlot.left;
for(i = m_rectPlot.top; i < m_rectPlot.bottom; i += 4)
m_dcGrid.SetPixel(GridPos, i, m_crGridColor);
}
*/
// create some fonts (horizontal and vertical)
// use a height of 14 pixels and 300 weight
// (these may need to be adjusted depending on the display)
axisFont.CreateFont(14, 0, 0, 0, 300,
//FALSE, FALSE, 0, ANSI_CHARSET,
FALSE, FALSE, 0, DEFAULT_CHARSET, // EC
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, "Arial");
yUnitFont.CreateFont(14, 0, 900, 0, 300,
//FALSE, FALSE, 0, ANSI_CHARSET,
FALSE, FALSE, 0, DEFAULT_CHARSET, // EC
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, "Arial");
// grab the horizontal font
oldFont = m_dcGrid.SelectObject(&axisFont);
// y max
m_dcGrid.SetTextColor(m_crGridColor);
m_dcGrid.SetTextAlign(TA_RIGHT | TA_TOP);
if(m_str.YMax.IsEmpty())
strTemp.Format("%.*lf", m_nYDecimals, m_PlotData[0].dUpperLimit);
else
strTemp = m_str.YMax;
m_dcGrid.TextOut(m_rectPlot.left - 4, m_rectPlot.top - 7, strTemp);
// y/2
strTemp.Format("%.*lf", m_nYDecimals, m_PlotData[0].dUpperLimit / 2);
m_dcGrid.TextOut(m_rectPlot.left - 2, m_rectPlot.bottom+ ((m_rectPlot.top - m_rectPlot.bottom)/2) - 7 , strTemp);
// y min
m_dcGrid.SetTextAlign(TA_RIGHT | TA_BASELINE);
if(m_str.YMin.IsEmpty())
strTemp.Format("%.*lf", m_nYDecimals, m_PlotData[0].dLowerLimit);
else
strTemp = m_str.YMin;
m_dcGrid.TextOut(m_rectPlot.left - 4, m_rectPlot.bottom, strTemp);
/*
// x min
m_dcGrid.SetTextAlign(TA_LEFT | TA_TOP);
if(m_str.XMin.IsEmpty())
m_dcGrid.TextOut(m_rectPlot.left, m_rectPlot.bottom + 4, "0");
else
m_dcGrid.TextOut(m_rectPlot.left, m_rectPlot.bottom + 4, (LPCTSTR)m_str.XMin);
// x max
m_dcGrid.SetTextAlign(TA_RIGHT | TA_TOP);
if(m_str.XMax.IsEmpty())
{
if(m_nTrendPoints < 0)
strTemp.Format("%d", m_nPlotWidth/m_nShiftPixels);
else
strTemp.Format("%d", m_nTrendPoints - 1);
}
else
strTemp = m_str.XMax;
m_dcGrid.TextOut(m_rectPlot.right, m_rectPlot.bottom + 4, strTemp);
*/
// x units
m_dcGrid.SetTextAlign(TA_CENTER | TA_TOP);
m_dcGrid.TextOut((m_rectPlot.left + m_rectPlot.right)/2,
m_rectPlot.bottom + 4, m_str.XUnits);
// restore the font
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -