📄 hmxchart.cpp
字号:
// HMXChart.cpp : implementation file
//
#include "stdafx.h"
#include "HMXChart.h"
#define HMX_AREA_MARGINS 80 // fraction of area
#define HMX_AREA_TITLE 10 // fraction of area
#define HMX_AREA_YAXIS 7 // percentage
#define HMX_AREA_XAXIS 10 // percentage
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CHMXChart
CHMXChart::CHMXChart()
{
// set defaul value
m_clrBkGnd = RGB( 200, 255, 255 );
m_strTitle = _T("");
m_strXText = _T("");
m_strYText = _T("");
m_nYTicks = 0;
m_bShowYScale = true;
m_nXMax = 0;
m_nYMin = 0;
m_nYMax = 0;
m_nXLabelStep = 1;
}
CHMXChart::~CHMXChart()
{
}
BEGIN_MESSAGE_MAP(CHMXChart, CWnd)
//{{AFX_MSG_MAP(CHMXChart)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CHMXChart message handlers
void CHMXChart::OnPaint()
{
CPaintDC dc(this); // device context for painting
CalcDatas();
PaintBkGnd( dc );
// dc.Rectangle( m_rectUsable );
// dc.Rectangle( m_rectYAxis );
// dc.Rectangle( m_rectXAxis );
// dc.Rectangle( m_rectData );
DrawTitle( dc );
DrawGrid( dc );
//DrawBaseline( dc );
DrawAxes( dc );
DrawYScale( dc );
DrawXScale( dc );
DrawDatasets( dc );
}
///////////////////////////////////////////////////////////////////////
//
// PaintBkGnd
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::PaintBkGnd(CDC &dc)
{
CBrush brsh( m_clrBkGnd );
CBrush* pOldBrush = dc.SelectObject( &brsh );
dc.Rectangle( m_rectArea );
dc.SelectObject( pOldBrush );
return true;
}
//
// DrawTitle
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawTitle(CDC & dc)
{
if( m_strTitle.IsEmpty() )
return false;
CFont font, *pFontOld;
font.CreateFont( m_rectTitle.Height(), 0, 0, 0, FW_NORMAL,
FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
DEFAULT_PITCH, "Arial");
COLORREF clrBkOld = dc.SetBkColor( m_clrBkGnd );
pFontOld = dc.SelectObject( &font );
dc.DrawText( m_strTitle, m_rectTitle, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
dc.SetBkColor( clrBkOld );
dc.SelectObject( pFontOld );
return true;
}
//
// DrawGrid
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawGrid(CDC & dc)
{
DrawVertLine( dc );
DrawHorzLine( dc );
return true;
}
//
// DrawAxes
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawAxes(CDC &dc)
{
// draw Y
dc.MoveTo( m_rectYAxis.right, m_rectYAxis.bottom );
dc.LineTo( m_rectYAxis.right, m_rectYAxis.top );
// draw X
dc.MoveTo( m_rectXAxis.left , m_rectXAxis.top );
dc.LineTo( m_rectXAxis.right, m_rectXAxis.top );
return true;
}
//
// DrawHorzLine
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawHorzLine(CDC & dc)
{
double nTicks = (double)GetYTicks();
if( !nTicks )
return false;
double nY = (m_nYMax - m_nYMin)/nTicks, nTemp;
int f;
CPen pen( PS_SOLID, 1, RGB(0,0,0)), *pPenOld;
pPenOld = dc.SelectObject( &pen );
for( f=0; f<=nTicks; f++ ) {
nTemp = m_rectData.bottom - ( nY*f ) * m_rectData.Height()/(m_nYMax-m_nYMin);
dc.MoveTo( m_rectData.left , (int)nTemp );
dc.LineTo( m_rectData.right, (int)nTemp );
}
dc.SelectObject( pPenOld );
return true;
}
//
// DrawVertLine
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawVertLine(CDC & dc)
{
if( !m_nXMax )
return false;
double nX = (double)m_rectData.Width()/(double)m_nXMax;
int f;
CPen pen( PS_SOLID, 1, RGB(0,0,0)), *pPenOld;
pPenOld = dc.SelectObject( &pen );
for( f=0; f<m_nXMax; f=f+m_nXLabelStep ) {
dc.MoveTo( m_rectData.left + (int)(nX*(f+0.5)), m_rectData.top );
dc.LineTo( m_rectData.left + (int)(nX*(f+0.5)), m_rectData.bottom );
}
dc.MoveTo( m_rectData.left + m_rectData.Width(), m_rectData.top );
dc.LineTo( m_rectData.left + m_rectData.Width(), m_rectData.bottom );
dc.SelectObject( pPenOld );
return true;
}
//
// DrawBaseLine
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawBaseline(CDC & dc)
{
CPen pen( PS_SOLID, 1, RGB(0,0,0)), *pPenOld;
pPenOld = dc.SelectObject( &pen );
// cannot draw baseline outside the m_rectData
if( m_nYMin > 0 )
return false;
double nTemp = ( - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin); // this is the zero baseline
dc.MoveTo( m_rectData.left , m_rectData.bottom - (int)nTemp );
dc.LineTo( m_rectData.right, m_rectData.bottom - (int)nTemp );
dc.SelectObject( pPenOld );
return true;
}
//
// DrawXScale
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawXScale(CDC & dc)
{
int nCount = m_strarrScaleXLabel.GetSize();
if( !nCount )
return false;
// nX is the size of a division
double nX = (double)m_rectData.Width()/(double)nCount;
int f, nFontSize = 12;//(int)(m_rectXAxis.Height()/3.0);
// nX = max( nX, nFontSize*2); // let me see everything
CRect rectTemp;
const int nBkModeOld = dc.SetBkMode( TRANSPARENT );
COLORREF clrBlack = RGB( 0, 0, 0), clrOld;
CPen pen(PS_SOLID, 3, clrBlack ), *pPenOld;
CFont font, *pFontOld;
font.CreateFont( nFontSize, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
DEFAULT_PITCH, "Arial");
pPenOld = dc.SelectObject(&pen);
pFontOld = dc.SelectObject(&font);
clrOld = dc.SetTextColor( clrBlack );
// draw text
for( f=0; f<nCount; f=f+m_nXLabelStep ) {
rectTemp.top = m_rectXAxis.top;
rectTemp.bottom = m_rectXAxis.bottom;
rectTemp.left = m_rectXAxis.left + (int)(nX*f);
rectTemp.right = m_rectXAxis.left + (int)(nX*(f+1));
dc.DrawText( m_strarrScaleXLabel.GetAt(f), rectTemp, DT_CENTER | DT_TOP | DT_SINGLELINE );
}
if( !m_strXText.IsEmpty() ) {
int nFontXTextSize = nFontSize*2;
CFont fontXText;
fontXText.CreateFont( nFontXTextSize, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
DEFAULT_PITCH, "Arial");
dc.SelectObject(&fontXText);
dc.DrawText( m_strXText, m_rectXAxis, DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
}
dc.SelectObject(pPenOld);
dc.SelectObject(pFontOld);
dc.SetTextColor(clrOld);
dc.SetBkMode(nBkModeOld);
return true;
}
//
// DrawYScale
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawYScale(CDC & dc)
{
if( !m_bShowYScale )
return false;
int nTicks;
if( !(nTicks = GetYTicks()) )
return false;
// nY is the size of a division
double nY = (m_nYMax - m_nYMin)/nTicks;
double nTemp1, nTemp2;
int f, nFontSize = 12;//(int)(m_rectYAxis.Width()/4);
CString sBuffer;
const int nBkModeOld = dc.SetBkMode( TRANSPARENT );
COLORREF clrBlack = RGB( 0, 0, 0), clrOld;
CPen pen(PS_SOLID, 3, clrBlack ), *pPenOld;
CFont font, *pFontOld;
font.CreateFont( nFontSize, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
DEFAULT_PITCH, "Arial");
pPenOld = dc.SelectObject(&pen);
pFontOld = dc.SelectObject(&font);
clrOld = dc.SetTextColor( clrBlack );
// draw text
for( f=0; f<=nTicks; f++ ) {
nTemp1 = m_rectYAxis.bottom + nFontSize/2 - ( nY*(f) ) * m_rectData.Height()/(m_nYMax-m_nYMin);
nTemp2 = m_rectYAxis.bottom + nFontSize/2 - ( nY*(f+1) ) * m_rectData.Height()/(m_nYMax-m_nYMin);
sBuffer.Format("%g", m_nYMin + nY*f );
dc.DrawText( sBuffer, CRect( m_rectYAxis.left,(int)nTemp2, m_rectYAxis.right, (int)nTemp1 ), DT_RIGHT | DT_BOTTOM | DT_SINGLELINE );
}
if( !m_strYText.IsEmpty() ) {
int nFontYTextSize = nFontSize*2;
CFont fontYText;
fontYText.CreateFont( nFontYTextSize, 0, 900, 0, FW_NORMAL,
FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
DEFAULT_PITCH, "Arial");
dc.SelectObject(&fontYText);
dc.DrawText( m_strYText, m_rectYAxis, DT_BOTTOM | DT_LEFT | DT_SINGLELINE );
}
dc.SelectObject(pPenOld);
dc.SelectObject(pFontOld);
dc.SetTextColor(clrOld);
dc.SetBkMode(nBkModeOld);
return true;
}
//
// DrawDatasets
//
// arguments
//
// dc = Decive Context
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawDatasets(CDC& dc)
{
int f;
// Draw dataset from last to first so I can show
// first dataset in foreground, below the second dataset and so on
for( f=HMX_MAX_DATASET-1; f>=0; f-- )
DrawDataset( dc, m_dataset[f] );
return true;
}
//
// DrawDataset
//
// arguments
//
// dc = Decive Context
// ds = Dataset
//
// return
//
// true if ok, else false
//
bool CHMXChart::DrawDataset(CDC &dc, CHMXDataset & ds)
{
// let's calc the bar size
double nBarWidth = (double)m_rectData.Width()/(double)m_nXMax;
int f, nMarkerType;
double nSample, nTemp, nTemp1, nZeroLine;
// get first sample, if dataset is empty continue to next dataset
f=0;
do {
if( !ds.GetData( f++, nSample ) )
return false;
} while( nSample == HMX_DATASET_VALUE_INVALID );
f--;
if( ds.GetStyle() == HMX_DATASET_STYLE_LINE ) {
// let's rock
CPen pen( PS_SOLID, ds.GetSize(), ds.GetColor() ), *pPenOld;
CBrush brush( ds.GetColor() ), *pBrushOld;
pPenOld = dc.SelectObject( &pen );
pBrushOld = dc.SelectObject( &brush );
// nTemp will contains a parametrized data
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
dc.MoveTo( m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f), m_rectData.bottom - (int)nTemp );
for( ; f<ds.GetDatasetSize(); f++ ) {
ds.GetData( f, nSample);
if( nSample != HMX_DATASET_VALUE_INVALID ) {
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
dc.LineTo( m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f), m_rectData.bottom - (int)(nTemp) );
}
}
nMarkerType = ds.GetMarker();
for( f=0; f<ds.GetDatasetSize(); f++ ) {
ds.GetData( f, nSample);
if( nMarkerType != HMX_DATASET_MARKER_NONE ) {
POINT* pPoint = new POINT[ 5 ];
ds.GetData( f, nSample);
if( nSample != HMX_DATASET_VALUE_INVALID ) {
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
switch( ds.GetMarker() ) {
case HMX_DATASET_MARKER_TRI:
pPoint[ 0 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f);
pPoint[ 0 ].y = m_rectData.bottom - (int)nTemp - ds.GetSize()*2;
pPoint[ 1 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) + ds.GetSize()*2;
pPoint[ 1 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
pPoint[ 2 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) - ds.GetSize()*2;
pPoint[ 2 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
dc.Polygon( pPoint, 3 );
break;
case HMX_DATASET_MARKER_BOX:
ds.GetData( f, nSample);
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[ 0 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) - ds.GetSize()*2;
pPoint[ 0 ].y = m_rectData.bottom - (int)nTemp - ds.GetSize()*2;
pPoint[ 1 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) + ds.GetSize()*2;
pPoint[ 1 ].y = m_rectData.bottom - (int)nTemp - ds.GetSize()*2;
pPoint[ 2 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) + ds.GetSize()*2;
pPoint[ 2 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
pPoint[ 3 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) - ds.GetSize()*2;
pPoint[ 3 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
dc.Polygon( pPoint, 4 );
break;
case HMX_DATASET_MARKER_SPH:
ds.GetData( f, nSample);
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[ 0 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) - ds.GetSize()*2;
pPoint[ 0 ].y = m_rectData.bottom - (int)nTemp - ds.GetSize()*2;
pPoint[ 1 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) + ds.GetSize()*2;
pPoint[ 1 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
dc.Ellipse( pPoint[0].x, pPoint[0].y, pPoint[1].x, pPoint[1].y );
break;
case HMX_DATASET_MARKER_DIA:
ds.GetData( f, nSample);
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[ 0 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f);
pPoint[ 0 ].y = m_rectData.bottom - (int)nTemp - ds.GetSize()*2;
pPoint[ 1 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) + ds.GetSize()*2;
pPoint[ 1 ].y = m_rectData.bottom - (int)nTemp;
pPoint[ 2 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f);
pPoint[ 2 ].y = m_rectData.bottom - (int)nTemp + ds.GetSize()*2;
pPoint[ 3 ].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f) - ds.GetSize()*2;
pPoint[ 3 ].y = m_rectData.bottom - (int)nTemp;
dc.Polygon( pPoint, 4 );
break;
}
}
delete []pPoint;
}
}
dc.SelectObject( pPenOld );
dc.SelectObject( pBrushOld );
}
if( ds.GetStyle() == HMX_DATASET_STYLE_VBAR ) {
// let's rock
CPen pen( PS_SOLID, 1, ds.GetColor() ), *pPenOld;
CBrush brush( ds.GetColor() ), *pBrushOld;
pPenOld = dc.SelectObject( &pen );
pBrushOld = dc.SelectObject( &brush );
CRect rectTemp;
// nTemp will contains a parametrized data
for( f=0; f<ds.GetDatasetSize(); f++ ) {
ds.GetData( f, nSample);
if( nSample == HMX_DATASET_VALUE_INVALID )
continue;
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
if( nSample > 0.0 ) {
// bar is positive
nZeroLine = m_nYMin > 0 ? m_nYMin : 0;
nTemp1 = ( nZeroLine -m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
rectTemp.top = m_rectData.bottom - (int)(nTemp);
rectTemp.bottom = m_rectData.bottom - (int)(nTemp1);
rectTemp.left = m_rectData.left + (int)(nBarWidth/2.0) - (int)(nBarWidth*(ds.GetSize()/2.0)/10.0) + (int)(nBarWidth*f);
rectTemp.right = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*(ds.GetSize()/2.0)/10.0) + (int)(nBarWidth*f);
// show at least 1 line bar
rectTemp.right += (rectTemp.right == rectTemp.left ) ? 1 : 0 ;
} else {
// bar is negative
nZeroLine = m_nYMax < 0 ? m_nYMax : 0;
nTemp1 = ( nZeroLine -m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
rectTemp.top = m_rectData.bottom - (int)(nTemp1);
rectTemp.bottom = m_rectData.bottom - (int)(nTemp);
rectTemp.left = m_rectData.left + (int)(nBarWidth/2.0) - (int)(nBarWidth*(ds.GetSize()/2.0)/10.0) + (int)(nBarWidth*f);
rectTemp.right = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*(ds.GetSize()/2.0)/10.0) + (int)(nBarWidth*f);
// show at least 1 line bar
rectTemp.right += (rectTemp.right == rectTemp.left ) ? 1 : 0 ;
}
dc.Rectangle( rectTemp );
}
dc.SelectObject( pPenOld );
dc.SelectObject( pBrushOld );
}
if( ds.GetStyle() == HMX_DATASET_STYLE_AREA ) {
int g;
// let's rock
CPen pen( PS_SOLID, 1, ds.GetColor() ), *pPenOld;
CBrush brush( ds.GetColor() ), *pBrushOld;
pPenOld = dc.SelectObject( &pen );
pBrushOld = dc.SelectObject( &brush );
// let's cale real dataset size (excluding invalid data)
int nPoints = 0;
for( g=0; g<ds.GetDatasetSize(); g++ ) {
ds.GetData( g, nSample );
if( nSample != HMX_DATASET_VALUE_INVALID )
nPoints++;
}
// add two points, for firs and last point
nPoints += 2;
// create a dynamic array
POINT* pPoint = new POINT[ nPoints ];
// the first
f=0;
do {
ds.GetData( f, nSample);
f++;
} while( nSample == HMX_DATASET_VALUE_INVALID );
pPoint[0].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*(f-1.0));
nZeroLine = m_nYMin > 0 ? m_nYMin : 0;
nZeroLine = m_nYMax < 0 ? m_nYMax : nZeroLine;
nTemp = ( nZeroLine -m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[0].y = m_rectData.bottom - (int)nTemp;
g = 1;
for( f=0; f<ds.GetDatasetSize(); f++ ) {
ds.GetData( f, nSample);
if( nSample == HMX_DATASET_VALUE_INVALID )
continue;
// nTemp will contains a parametrized data
nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[g].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*f);
pPoint[g].y = m_rectData.bottom - (int) nTemp;
g++;
}
// the last
// pPoint[nPoints-1].x = m_rectData.left + (int)(nBarWidth/2.0) + (int)(nBarWidth*(g-2));
pPoint[nPoints-1].x = pPoint[g-1].x;
nZeroLine = m_nYMin > 0 ? m_nYMin : 0;
nZeroLine = m_nYMax < 0 ? m_nYMax : nZeroLine;
nTemp = ( nZeroLine - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
pPoint[nPoints-1].y = m_rectData.bottom - (int) nTemp;
dc.Polygon( pPoint, nPoints );
dc.SelectObject( pPenOld );
dc.SelectObject( pBrushOld );
delete []pPoint;
}
return true;
}
//
// CalcDatas
// calculate all useful variables starting from the control size
//
// arguments
//
// none
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -