📄 clplot.cpp
字号:
//*******************************************************************************************************/
//* FileName: clPlot.cpp
//*
//* Contents: Implementation of clPlot, axis, legend, serie and timeaxis
//*
//* NOTE 1: Only a minimum of parameter validation is implemented to save time since this plot is
//* time critical.
//*
//* NOTE 2: All functionality is not fully implemented.
//*
//* Author: Jan Vidar Berger
//*******************************************************************************************************/
//* 12.feb.98 Jan Vidar Berger Implemented flicker free drawing. Thanks to John Kim for providing
//* the MemDC and to Keith Rule, the author of CMemDC.
//*******************************************************************************************************/
#include "stdafx.h"
#include "clPlot.h"
#include "MemDC.h"
#include "afx.h"
#include "malloc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
long clPlot::m_lMaxDataPrSerie; // max allowed data pr. serie.
long clPlot::m_lMaxDataTotal; // max allowed data total.
//*******************************************************************************************************/
//* time axis threshold. contains grid and label intervals to be used within specified
//* seconds pr. pixels thresholds. The array is terminated by a 'bIAmInUse'=FALSE.
//*******************************************************************************************************/
struct{
BOOL bIAmInUse; // indicate valid entry, last=FALSE
long lgridinterval; // grid line interval in seconds
long llabelinterval; // time label interval in seconds
long lmodethreshold; // mode threshold in seconds pr. pixel
}gridsetting[]={
TRUE, 1, 4, 0, // 0: pr. second
FALSE, 1, 1,0, // last entry in table
};
//*******************************************************************************************************/
//* Function: serie::serie
//*******************************************************************************************************/
serie::serie()
{
m_bIAmInUse = FALSE;
m_color = RGB(0,0,0);
m_iLineStyle = PS_SOLID;
m_bRightAxisAlign = FALSE;
m_lNoValues = 0;
m_lbegin = 0;
m_lend = 0;
m_pvalues = NULL;
}
//*******************************************************************************************************/
//* Function: serie::~serie
//*******************************************************************************************************/
serie::~serie()
{
if(m_pvalues !=NULL)
free(m_pvalues);
}
//*******************************************************************************************************/
//* Function: serie::AddPoint
//*
//* Description: AddPoint add new data to the end of a data serie. It will simply append the data,
//* update the list index and get out.
//*
//* This function will also call realloc or malloc to re-size or create the plot array as
//* needed.
//*
//* The nice thing about circular lists is that they are multi thread enabled as is. You
//* must however implement a syncronization mechanism if more than one thread is supposed
//* to enter data into the plot.
//*
//* Parameters: valuetime Time (x value).
//* y y value
//*
//* Return Value: -none-
//*
//* Author: Jan Vidar Berger
//*******************************************************************************************************/
//动态调整缓冲数组空间,然后在数组末尾插入新数据。
void serie::AddPoint(long &valuetime , double &y)
{
if(m_lNoValues > 0)
{
if(m_lNoValues <= clPlot::m_lMaxDataPrSerie)
m_pvalues = (value*)realloc(m_pvalues, (m_lNoValues+1)*sizeof(value));//重新调整数组大小
}
else
m_pvalues = (value*)malloc((m_lNoValues+1)*sizeof(value));//创建数组
m_pvalues[m_lend].ValueTime = valuetime;//X axis value
m_pvalues[m_lend].dValue = y; //Y axis value
m_lNoValues++; //数据总个数
TRACE(" begin=%d ,end=%d valueNos=%d \n", m_lbegin,m_lend,m_lNoValues);
m_lend++;
//if(m_lend >= clPlot::m_lMaxDataPrSerie)//m_lend最大不超过clPlot::m_lMaxDataPrSerie
// m_lend=0;
m_lend =m_lend%( clPlot::m_lMaxDataPrSerie);//m_lend最大不超过clPlot::m_lMaxDataPrSerie
if(m_lbegin == m_lend)
{
m_lbegin++; //此时采取数据个数〉m_lMaxDataPrSerie,数据循环存放在m_pvalues[]中
if(m_lbegin >= clPlot::m_lMaxDataPrSerie)
m_lbegin=0;
}
}
void serie::Serialize(CArchive &ar)
{
if(!m_bIAmInUse) return;
long i,j,k;
int nos;
if(ar.IsStoring())
{
nos=(m_lend-m_lbegin+clPlot::m_lMaxDataPrSerie)%clPlot::m_lMaxDataPrSerie;
ar<<nos;//数据个数
for(i=m_lbegin,j=0;j<nos;i++,j++)
{
k=i%( clPlot::m_lMaxDataPrSerie);
ar<<m_pvalues[k].ValueTime<<m_pvalues[k].dValue;//保存采样值(时间,速度)
}
}
else
{
ar>>nos;//数据个数
if(!nos) return;
m_lbegin=0;
m_lend=nos;
if(m_lNoValues >0)
m_pvalues = (value*)realloc(m_pvalues, (nos+1)*sizeof(value));//重新调整数组大小
else
{
m_pvalues = (value*)malloc((nos+1)*sizeof(value));//创建数组
}
m_lNoValues=nos;
for(i=m_lbegin,j=0;j<nos;i++,j++)
{
k=i%( clPlot::m_lMaxDataPrSerie);
ar>>m_pvalues[k].ValueTime>>m_pvalues[k].dValue;//装入采样值(时间,速度)
}
}
}
//*******************************************************************************************************/
//* Function: serie::Reset
//*
//* Description: Reset the serie. Remove data and reset indexes and pointers.
//*
//* Parameters: -none-
//*
//* Return Value: -none-
//*
//* Author: Jan Vidar Berger
//*******************************************************************************************************/
void serie::Reset()
{
m_lNoValues=0;
if(m_pvalues !=NULL)
free(m_pvalues);
m_pvalues = NULL;
m_lbegin = 0;
m_lend = 0;
}
//*******************************************************************************************************/
//*******************************************************************************************************/
IMPLEMENT_SERIAL(clPlot, CWnd, 0)
clPlot::clPlot()
{
m_ctlBkColor = RGB(255,255,255);
//m_ctlBkColor = RGB(64,128,128);
m_plotBkColor = RGB(64,128,128);
m_plotBkColor = RGB(255,255,255);
m_legendBkColor = RGB(255,255,255);
m_gridColor = RGB(127,127,127);
m_bctlBorder = TRUE;
m_bplotBorder = TRUE;
m_blegendBorder = TRUE;
m_bPrimaryLegend = FALSE;
m_bSecondaryLegend = FALSE;
m_bAxisLY = TRUE;
m_bAxisRY = TRUE;
m_bAxisBX = TRUE;
m_bAutoScrollX = FALSE;
m_bSimMode = FALSE;
m_bXThinGrid=false;//默认方式只画栅格主线,不画辅线
m_bYThinGrid=false;
m_bXThickGrid=true;
m_bYThickGrid=true;
m_bDrawLegend=false;//默认方式不显示图例
m_bDispXTitle=true; //是否显示X轴单位(默认显示)
m_bDispYTitle=true; //是否显示Y轴单位(默认显示)
m_txtColor=RGB(0,0,255);//blue. 标识符(刻度,X,Y轴名称及单位)颜色
m_bLeftYAutoAdjust=true;//Y轴刻度坐标是否自动调整(默认自动)
m_lMaxDataPrSerie = 10000; //单条曲线数组最多容纳的数据个数
m_lMaxDataTotal = 100000;//单条曲线最多允许的数据个数(目前改变量没用上)
//m_lMaxDataPrSerie = 5; //单条曲线数组最多容纳的数据个数
//m_lMaxDataTotal = 10;//单条曲线最多允许的数据个数(目前改变量没用上)
m_dNoData = 0.0;
m_dzoom = 1.0;
lArraySize = 1000; // only points with differebt x,y will be put into the array
pLineArray = new CPoint[lArraySize];
long totime,fromtime;
fromtime=0;totime=30000;//ms
SetBXRange(fromtime,totime);
m_logFont.lfHeight = -13;
m_logFont.lfHeight = 16;
m_logFont.lfWidth = 0;
m_logFont.lfEscapement = 0;
m_logFont.lfOrientation = 0;
m_logFont.lfWeight = 600;
m_logFont.lfItalic = FALSE;
m_logFont.lfUnderline = FALSE;
m_logFont.lfStrikeOut = FALSE;
m_logFont.lfCharSet = ANSI_CHARSET;
//m_logFont.lfCharSet = GB2312_CHARSET;
m_logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
m_logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
m_logFont.lfQuality = PROOF_QUALITY;
m_logFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(m_logFont.lfFaceName,"Arial");
m_zoomFont.lfHeight = -13;
m_zoomFont.lfWidth = 16;
m_zoomFont.lfEscapement = 0;
m_zoomFont.lfOrientation = 0;
m_zoomFont.lfWeight = 600;
m_zoomFont.lfItalic = FALSE;
m_zoomFont.lfUnderline = FALSE;
m_zoomFont.lfStrikeOut = FALSE;
m_zoomFont.lfCharSet = ANSI_CHARSET;
//m_zoomFont.lfCharSet =GB2312_CHARSET;
m_zoomFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
m_zoomFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
m_zoomFont.lfQuality = PROOF_QUALITY;
m_zoomFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(m_zoomFont.lfFaceName,"Arial");
m_bUseRightYAxis=false;//单Y轴
m_bRightYMargin=true;//右Y轴留空格
//坐标图上当前点距离右轴占整个X轴长度的百分比,这样做便于看到后面曲线的期望值
m_dMarginPercentOfCurrentPointToRightY=0.15;//默认为0.3
}
//*******************************************************************************************************/
//*******************************************************************************************************/
clPlot::~clPlot()
{
Reset();
delete [] pLineArray;
}
//*******************************************************************************************************/
//*******************************************************************************************************/
BEGIN_MESSAGE_MAP(clPlot, CWnd)
//{{AFX_MSG_MAP(clPlot)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//*******************************************************************************************************/
//*******************************************************************************************************/
BOOL clPlot::Create(DWORD dwstyle, CRect &rect, CWnd *pParent, UINT id)
{
DWORD style = dwstyle & (~WS_BORDER);
if(dwstyle & WS_BORDER)
m_bctlBorder=TRUE;
else
m_bctlBorder=FALSE;
//m_ctlBkColor=(COLORREF)::GetSysColor(COLOR_3DFACE);//默认方式与背景FACE颜色一致(即:透明)
CString wndclass = AfxRegisterWndClass(CS_DBLCLKS, LoadCursor(NULL, IDC_ARROW),
CreateSolidBrush(GetSysColor(COLOR_BTNFACE)), 0);
//if(!CWnd::Create(wndclass, "long", style, rect, pParent, id, NULL))
if(!CWnd::Create(NULL, "", style, rect, pParent, id, NULL))
{
AfxMessageBox("创建窗口失败");
return FALSE;
}
m_ctlRect = rect;//(父窗口的)逻辑坐标
pParent->ClientToScreen(m_ctlRect);//(父窗口的)设备坐标
ScreenToClient(m_ctlRect);//(当前窗口的)逻辑坐标
ComputeRects(TRUE);//
return TRUE;
}
//*******************************************************************************************************/
//* Function : clPlot::ComputeRects
//*
//* Description : Compute rects used for internal possitioning of different objects. This function is
//* called when the plot is created or sized.
//*
//* Return type : void
//*
//* Parameter(s) : bInitialization indicate wherever parameters that can be changed abu the user
//* also should be computed.
//*
//* Author : Jan Vidar Berger
//*******************************************************************************************************/
void clPlot::ComputeRects(BOOL bInitialization,CDC *printerDC)
{
// adjust the client rect for borders
CClientDC dc(this);
int w = 0;
int n=0;
CSize z;
if(printerDC!=NULL)
{
z=printerDC->GetTextExtent(CString("A"));
m_dzoom = ((double)m_ctlRect.Height()/(double)z.cy) / 15.0;
}
else
{
z=dc.GetTextExtent(CString("A"));
m_dzoom = ((double)m_ctlRect.Height()/(double)z.cy) / 25.0;
}
m_zoomFont.lfWidth = (int)(m_logFont.lfWidth * m_dzoom);
m_zoomFont.lfHeight = (int)(m_logFont.lfHeight * m_dzoom);
CFont *oFont;
CFont newfont;
if(printerDC!=NULL)
{
if(!newfont.CreateFontIndirect(&m_zoomFont))
{
MessageBox("create font failed");
}
}
if(printerDC!=NULL)
{
oFont = printerDC->SelectObject(&newfont);
z=printerDC->GetTextExtent(CString("A"));
}
else
{
oFont = dc.SelectObject(&newfont);
z=dc.GetTextExtent(CString("A"));
}
m_TextHeight = z.cy;
if(m_bctlBorder)
{
m_clientRect.left = m_ctlRect.left+2;
m_clientRect.right = m_ctlRect.right-2;
m_clientRect.top = m_ctlRect.top+2;
m_clientRect.bottom = m_ctlRect.bottom-2;
}
else
{
m_clientRect = m_ctlRect;
}
if(bInitialization)
{
m_iMtop = m_clientRect.Height()/10;
m_iMbottom = m_clientRect.Height()/8;
m_iMleft = m_clientRect.Width()/10;
if(m_bUseRightYAxis|m_bRightYMargin)
m_iMright = m_clientRect.Width()/10;
else
m_iMright = m_clientRect.Width()/15;
}
// compute plot rect.
m_plotRect.left= m_clientRect.left + m_iMleft;
m_plotRect.right= m_clientRect.right - m_iMright;
m_plotRect.top= m_clientRect.top + m_iMtop;
m_plotRect.bottom= m_clientRect.bottom - m_iMbottom;
// compute default legend possition
if(bInitialization)
{
m_legendRect.left = m_plotRect.left + (m_iMleft/5);
m_legendRect.right = m_plotRect.left + (m_plotRect.Width()/5);
m_legendRect.top = m_plotRect.top - (m_iMtop/2);
m_legendRect.bottom = m_plotRect.top + (m_iMtop);
int w = 0;
int n=0;
for(int x = 0; x< MAXLEGENDS;x++){
if(m_primarylegends[x].m_bIAmInUse){
n++;
z=dc.GetTextExtent(CString(m_primarylegends[x].m_szTitle));
if(z.cx > w )
w=z.cx;
// m_TextHeight = z.cy;
}
}
m_legendRect.right = m_legendRect.left + 40 + w;
m_legendRect.bottom = m_legendRect.top + 10 + (m_TextHeight*n);
}
// compute left axis area
m_axisLYRect.left = m_clientRect.left + (m_iMleft/5);
m_axisLYRect.right = m_plotRect.left;
m_axisLYRect.top = m_plotRect.top;
m_axisLYRect.bottom = m_plotRect.bottom;
// compute right axis area
m_axisRYRect.left = m_plotRect.left;
m_axisRYRect.right = m_clientRect.right - (m_iMright/5);
m_axisRYRect.top = m_plotRect.top;
m_axisRYRect.bottom = m_plotRect.bottom;
// compute bottom axis area
m_axisBXRect.left = m_plotRect.left;
m_axisBXRect.right = m_plotRect.right;
m_axisBXRect.top = m_plotRect.bottom;
m_axisBXRect.bottom = m_clientRect.bottom - (m_iMbottom/5);
//if(bInitialization)
{
//屏幕上X轴每象素代表的时间(秒)
//m_timeaxis.m_dMilliSecondsPrPixel = ((double)(m_timeaxis.m_maxtime.GetTime() - m_timeaxis.m_mintime.GetTime())) / (double)m_plotRect.Width();
m_timeaxis.m_dMilliSecondsPrPixel = ((double)(m_timeaxis.m_maxtime - m_timeaxis.m_mintime)) / (double)m_plotRect.Width();
//m_timeaxis.m_dMilliSecondsPrPixel = (m_timeaxis.m_maxtime - m_timeaxis.m_mintime) / m_plotRect.Width();
//屏幕上左Y轴每象素代表的实际值
m_leftaxis.m_dValuePrPixel = ((double)(m_leftaxis.maxrange- m_leftaxis.minrange) / (double)m_plotRect.Height());
//屏幕上右Y轴每象素代表的实际值
m_rightaxis.m_dValuePrPixel = ((double)(m_rightaxis.maxrange- m_rightaxis.minrange) / (double)m_plotRect.Height());
}
m_iXBigGrids=6; //X轴分为m_iXBigGrid个大网格(模认为6)
m_iYBigGrids=5; //Y轴分为m_iYBigGrid个大网格(模认为5)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -