Click here to Skip to main content
15,880,956 members
Articles / Desktop Programming / MFC

Chart and Pie for data with hole

Rate me:
Please Sign up or sign in to vote.
4.25/5 (5 votes)
3 May 2002 84.5K   3.3K   42  
Chart and Pie for data with hole.
// DataChart.cpp: implementation of the CDataChart class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "chart.h"
#include "DataChart.h"

#include <math.h>

#define AREA_MARGINS	80		// fraction of area
#define AREA_TITLE		10		// fraction of area
#define AREA_YAXIS		7		// percentage
#define AREA_XAXIS		10		// percentage
#define AREA_LEGEND		30		// Maximum percentage for legend

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CDataChart

CDataChart::CDataChart()
{
	// set defaul value

	m_nStyle = STYLE_LINE;

	m_clrBkGnd = RGB( 255, 255, 255 );
	m_strTitle = _T("");
	m_strXText = _T("");
	m_strYText = _T("");
	m_nYTicks = 20;

	m_bShowYScale = true;
	m_bShowXScale = true;
	m_bShowBaseLine = false;
	m_bShowGrid = false;
	m_bShowLegend = true;

	m_nRoundY = 10;
	m_nXMin = 32768;
	m_nXMax = 0;
	m_nYMin = 0;
	m_nYMax = 0;
	m_nXLabelStep = 1;

	m_dataset = (List *)new List();

	srand( (unsigned)time( NULL ) );
}

CDataChart::~CDataChart()
{
	delete m_dataset;
}


BEGIN_MESSAGE_MAP(CDataChart, CWnd)
	//{{AFX_MSG_MAP(CDataChart)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CDataChart message handlers

void CDataChart::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	if(m_nStyle == STYLE_LINE)
	{
		CalcDatas();
		DrawLine(dc);
	}
	else if(m_nStyle == STYLE_PIE)
		DrawPie(dc);

}


bool CDataChart::CopyToClipboard()
{
	//
	// Special thanks to Zafir Anjum for a large part of following code.
	//
	CBitmap         bitmap;
	CClientDC       dc(this);
	CDC             memDC;

	BeginWaitCursor();

	memDC.CreateCompatibleDC(&dc);

	bitmap.CreateCompatibleBitmap(&dc, m_rectArea.Width(),m_rectArea.Height() );

	CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);

	if(m_nStyle == STYLE_LINE)
		DrawLine(memDC);
	else if(m_nStyle == STYLE_PIE)
		DrawPie(memDC);


	this->OpenClipboard() ;
	EmptyClipboard() ;
	SetClipboardData (CF_BITMAP, bitmap.GetSafeHandle() ) ;
	CloseClipboard () ;

	bitmap.Detach();

	EndWaitCursor();

	return true;
}


///////////////////////////////////////////////////////////////////////

//
//	PaintBkGnd
//
//	arguments
//
//		dc = Decive Context
//
//	return
//
//		true if ok, else false
//
bool CDataChart::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 CDataChart::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;
}



//
//	DrawHorzLine
//
//	arguments
//
//		dc = Decive Context
//
//	return
//
//		true if ok, else false
//
bool CDataChart::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 CDataChart::DrawVertLine(CDC & dc)
{
	if( !m_nXMax )
		return false;

	int f, nCount, offset;
	nCount = (int)((double)(m_nXMax - m_nXMin)/(double)m_nXLabelStep);

	CPen pen( PS_SOLID, 1, RGB(0,0,0)), *pPenOld;
	pPenOld = dc.SelectObject( &pen );

	for( f=0; f<=nCount; f++) {
		offset = (int)((double)m_rectData.Width()/(double)nCount*(double)f);
		dc.MoveTo( m_rectData.left + offset, m_rectData.top );
		dc.LineTo( m_rectData.left + offset, 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;
}


//
//	DrawXScale
//
//	arguments
//
//		dc = Decive Context
//
//	return
//
//		true if ok, else false
//
bool CDataChart::DrawXScale(CDC & dc)
{
	if(!m_nXLabelStep)
		return false;
	
	int nCount = (int)((double)(m_nXMax - m_nXMin)/(double)m_nXLabelStep);
	int f, nFontSize = (int)(m_rectXAxis.Height()/3.0);
	double nBar;

	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;
	CString sBuffer;

	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 );

	nBar = (double)m_rectData.Width()/(double)(nCount*2);

	// draw text
	for( f=0; f<=nCount; f++ ) {
		rectTemp.top    = m_rectXAxis.top;
		rectTemp.bottom = m_rectXAxis.bottom;
		rectTemp.left   = m_rectXAxis.left + (int)((double)m_rectData.Width()/(double)nCount*(double)f - nBar);
		rectTemp.right  = m_rectXAxis.left + (int)((double)m_rectData.Width()/(double)nCount*(double)f + nBar);
		sBuffer.Format("%d", m_nXMin + f*m_nXLabelStep );

		dc.DrawText( sBuffer, 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 CDataChart::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, nTemp1, nTemp2;
	int f, nFontSize = (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;
}


//
//	DrawDataset
//
//	arguments
//
//		dc = Decive Context
//		ds = Dataset
//
//	return
//
//		true if ok, else false
//
bool CDataChart::DrawDataset(CDC &dc, CDataSeries *ds)
{
	// let's calc the bar size
	double nBarWidth, nSample, nTemp;
	int	i, f, nXTemp, nCount;

	nCount = (int)((double)(m_nXMax - m_nXMin)/(double)m_nXLabelStep);
	nBarWidth = (double)m_rectData.Width()/(double)nCount;

	if(!ds->Get(0, f, nSample))
		return true;

	// 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 
	nXTemp = (int)((double)m_rectData.Width()/(double)(m_nXMax-m_nXMin)*(double)(f-m_nXMin));
	nTemp = ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
	dc.MoveTo( m_rectData.left + nXTemp, m_rectData.bottom - (int)nTemp );
	for( i = 0; i < ds->GetDatasetSize(); i++ )
	{
		ds->Get(i, f, nSample);
		nXTemp = (int)((double)m_rectData.Width()/(double)(m_nXMax-m_nXMin)*(double)(f-m_nXMin));
		nTemp =  ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
		dc.LineTo( m_rectData.left + nXTemp, m_rectData.bottom - (int)(nTemp) );
	}
	dc.SelectObject( pPenOld );
	dc.SelectObject( pBrushOld );

	for( i=0; i<ds->GetDatasetSize(); i++ )
	{
		ds->Get(i, f, nSample);
		if( ds->GetMarker() != DATASET_MARKER_NONE )
		{
			nXTemp = (int)((double)m_rectData.Width()/(double)(m_nXMax-m_nXMin)*(double)(f-m_nXMin));
			nTemp =  ( nSample - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin);
			DrawLinMarker(dc, ds, m_rectData.left + nXTemp, m_rectData.bottom - (int)nTemp);
		}
	}

	return true;
}

//
//	CalcDatas
//	calculate all useful variables starting from the control size
//
//	arguments
//
//		none
//
//	return
//
//		true if ok, else false
//
bool CDataChart::CalcDatas()
{
	double nTemp1, nTemp2;
	int f=0, nTemp3, nTemp4;
	CDataSeries *pdata;

	GetClientRect( m_rectArea );

	m_rectUsable.top    = m_rectArea.top    + m_rectArea.Height()/AREA_MARGINS;
	m_rectUsable.bottom = m_rectArea.bottom - m_rectArea.Height()/AREA_MARGINS;
	m_rectUsable.left   = m_rectArea.left   + m_rectArea.Width() /AREA_MARGINS;
	m_rectUsable.right  = m_rectArea.right  - m_rectArea.Width() /AREA_MARGINS;

	// let's calc everything
	if( !m_strTitle.IsEmpty() ) {
		m_rectTitle.top    = m_rectUsable.top;
		m_rectTitle.left   = m_rectUsable.left;
		m_rectTitle.bottom = m_rectUsable.bottom/AREA_TITLE;
		m_rectTitle.right  = m_rectUsable.right;

		m_rectGraph.top    = m_rectTitle.bottom;
		m_rectGraph.left   = m_rectUsable.left;
		m_rectGraph.bottom = m_rectUsable.bottom;
		m_rectGraph.right  = m_rectUsable.right;
	} else {
		m_rectGraph = m_rectUsable;
	}

	m_rectYAxis.top    = m_rectGraph.top;
	m_rectYAxis.left   = m_rectGraph.left;
	m_rectYAxis.bottom = m_rectGraph.top  + m_rectGraph.Height()*(100-AREA_XAXIS)/100;
	m_rectYAxis.right  = m_rectGraph.left + m_rectGraph.Width()*(AREA_YAXIS)/100;

	CalcLegend();

	m_rectXAxis.top    = m_rectGraph.top  + m_rectGraph.Height()*(100-AREA_XAXIS)/100;
	m_rectXAxis.left   = m_rectGraph.left + m_rectGraph.Width()*(AREA_YAXIS)/100;
	m_rectXAxis.bottom = m_rectGraph.bottom;
	m_rectXAxis.right  = m_rectGraph.right - m_rectLegend.Width() - 5;

	m_rectData.top     = m_rectGraph.top;
	m_rectData.bottom  = m_rectXAxis.top;
	m_rectData.left    = m_rectYAxis.right;
	m_rectData.right   = m_rectGraph.right - m_rectLegend.Width() - 5;


	for(f = 0; f < m_dataset->size(); f++)
	{
		pdata = (CDataSeries *) m_dataset->get(f);

		pdata->GetXMinMax(nTemp3, nTemp4);
		m_nXMin = min( m_nXMin ,nTemp3 );
		m_nXMax = max( m_nXMax, nTemp4 );

		pdata->GetYMinMax(nTemp1, nTemp2);
		m_nYMin = min( m_nYMin ,nTemp1 );
		m_nYMax = max( m_nYMax, nTemp2 );
	}

	// Hey man, there's nothing to draw
	if( m_nXMax == 0 )
		return false;

	if((m_nXMax-m_nXMin)%m_nXLabelStep != 0)
		m_nXMax = ((m_nXMax - m_nXMin)/m_nXLabelStep + 1)*m_nXLabelStep + m_nXMin;

	// now I modify m_nYMin & m_nXMax to improve readability
	m_nYMin -= (m_nYMax - m_nYMin)*0.0; 
	m_nYMax += (m_nYMax - m_nYMin)*0.0; 

	// whid this 'strange' function I can set m_nYmin & m_nYMax so that 
	// they are multiply of m_nRoundY
	if( m_nRoundY > 0.0 ) {
		if(m_nYMin >= 0)
			m_nYMin = 0;
		else
			m_nYMin = (((int)m_nYMin-(int)m_nRoundY)/(int)m_nRoundY)*m_nRoundY;

		m_nYMax = (((int)m_nYMax+(int)m_nRoundY)/(int)m_nRoundY)*m_nRoundY;
	}

	// now nYMin & nYMax contain absolute min and absolute max
	// and these data can be used to calc the graphic's Y scale factor
	// nXMax contains the maximum number of elements, useful to 
	// calculate the X scale factor

	return true;
}

//
//	Redraw
//	redraw everything
//
//	arguments
//
//		none
//
//	return
//
//		true if ok, else false
//
bool CDataChart::Redraw()
{

	Invalidate(true);
	GetParent()->SendMessage( WM_PAINT, 0, 0 );

	return true;
}



CDataSeries	* CDataChart::LookUp(int nDatasetIndex)
{
	CDataSeries *pdata;
	for(int i = 0; i < m_dataset->size(); i++)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		if(pdata->GetID() == nDatasetIndex)
			return pdata;
	}

	return NULL;
}



//
//	SetData
//	Append a data into the dataset
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nIndex        = index
//		nData         = value
//
//	return
//
//		true if ok, else false
//
void CDataChart::Add(int nDatasetIndex, int nIndex, double nData)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		pdata->Add(nIndex, nData);
		return;
	}
	else
	{
		CString label;
		pdata = (CDataSeries *) new CDataSeries();
		pdata->SetID(nDatasetIndex);
		pdata->Add(nIndex, nData);

		m_dataset->add(pdata);

		label = pdata->GetLabel();
		if(label.IsEmpty())
		{
			label.Format("Data Series %d", nDatasetIndex);
			pdata->SetLabel(label);
		}
	}

	return;
}


//
//	SetData
//	Modify a data into the dataset
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nIndex        = index
//		nData         = value
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetData(int nDatasetIndex, int nIndex, double nData)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->SetData(nIndex, nData);

	return false;
}

//
//	GetData
//	get data from the dataset
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nIndex        = index
//		nData         = value
//
//	return
//
//		true if ok, else false
//
bool CDataChart::GetData(int nDatasetIndex, int nIndex, double& nData)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->GetData(nIndex, nData);

	return false;
}

//
//	SetDatasetStyle
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nStyle        = style
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetStyle(int nStyle)
{
	m_nStyle = nStyle;

	return true;
}

//
//	GetDatasetStyle
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nStyle        = style
//
//	return
//
//		true if ok, else false
//
bool CDataChart::GetStyle(int& nStyle)
{
	nStyle = m_nStyle;
	return true;
}


bool CDataChart::SetDatasetLabel( int nDatasetIndex, CString sLabel )
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		pdata->SetLabel(sLabel);
		return true;
	}

	return false;
}

bool CDataChart::GetDatasetLabel( int nDatasetIndex, CString& sLabel )
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		sLabel = pdata->GetLabel();
		return true;
	}

	return false;
}

//
//	SetDatasetMarker
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nMarker       = marker
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetDatasetMarker(int nDatasetIndex, int nMarker)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->SetMarker(nMarker);

	return false;
}

bool CDataChart::SetDatasetMarkerRatio(int nDatasetIndex, int nRatio)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->SetMarkerRatio(nRatio);

	return false;
}


//
//	GetDatasetMarker
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nMarker       = marker
//
//	return
//
//		true if ok, else false
//
bool CDataChart::GetDatasetMarker(int nDatasetIndex, int& nMarker)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		nMarker = pdata->GetMarker();
		return true;
	}

	return false;
}


bool CDataChart::GetDatasetMarkerRatio(int nDatasetIndex, int& nRatio)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		nRatio = pdata->GetMarkerRatio();
		return true;
	}

	return false;
}


//
//	SetDatasetPenSize
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nSize         = pen size in pixel or bar size (range 1-10)
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetDatasetPenSize(int nDatasetIndex, int nSize)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->SetSize(nSize);

	return false;
}

//
//	GetDatasetPenSize
//
//	arguments
//
//		nDatasetIndex = dataset index
//		nSize         = size
//
//	return
//
//		true if ok, else false
//
bool CDataChart::GetDatasetPenSize(int nDatasetIndex, int& nSize)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		nSize = pdata->GetSize();
		return true;
	}

	return false;
}

//
//	SetDatasetPenColor
//
//	arguments
//
//		nDatasetIndex = dataset index
//		clr           = color
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetDatasetPenColor(int nDatasetIndex, COLORREF clr)
{
		CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
		return pdata->SetColor(clr);

	return false;
}

//
//	GetDatasetPenColor
//
//	arguments
//
//		nDatasetIndex = dataset index
//		clr           = color
//
//	return
//
//		true if ok, else false
//
bool CDataChart::GetDatasetPenColor(int nDatasetIndex, COLORREF& clr)
{
	CDataSeries *pdata;

	if((pdata = LookUp(nDatasetIndex)) != NULL)
	{
		clr = pdata->GetColor();
		return true;
	}

	return false;
}

//
//	SetBkGnd
//
//	arguments
//
//		clr        = color
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetBkGnd(COLORREF clr)
{
	m_clrBkGnd = clr;
	Redraw();

	return true;
}

//
//	SetBkGnd
//
//	arguments
//
//		none
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetBkGnd()
{
	CColorDialog dlg( GetBkGnd() );

	if( dlg.DoModal() == IDOK )
		return SetBkGnd( dlg.GetColor() );
	else
		return false;

}

//
//	GetBkGnd
//
//	arguments
//
//		none
//
//	return
//
//		background color
//
COLORREF CDataChart::GetBkGnd()
{
	return m_clrBkGnd;
}

//
//	SetTitle
//
//	arguments
//
//		sTitle = title
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetTitle(CString strTitle)
{
	m_strTitle = strTitle;
	Redraw();

	return true;
}

//
//	GetTitle
//
//	arguments
//
//		none
//
//	return
//
//		title
//
CString CDataChart::GetTitle()
{
	return m_strTitle;
}

//
//	SetYTicks
//
//	arguments
//
//		nTicks = Y divisions (or ticks) in range 0-100
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetYTicks(int nTicks)
{
	m_nYTicks = nTicks;
	m_nYTicks = min( m_nYTicks, 100 );
	m_nYTicks = max( m_nYTicks, 0 );

	return true;
}

//
//	GetYTicks
//
//	arguments
//
//		none
//
//	return
//
//		nTicks
//
int CDataChart::GetYTicks()
{
	return m_nYTicks;
}


//
//	SetRoundY
//	let's try to improve readability
//
//	arguments
//
//		nRoundY	= rounding value
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetRoundY(double nRound)
{
	if( nRound <= 0)
		return false;
	
	m_nRoundY = nRound;

	return true;
}

//
//	GetRoundY
//
//	arguments
//
//		none
//
//	return
//
//		nRoundY = rounding value
//
double CDataChart::GetRoundY()
{
	return m_nRoundY;
}

//
//	SetYText
//
//	arguments
//
//		sText	= text
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetYText(CString sText)
{
	m_strYText = sText;
	Redraw();

	return true;
}

//
//	GetYText
//
//	arguments
//
//		none
//
//	return
//
//		sText	= text
//
CString CDataChart::GetYText()
{
	return m_strYText;
}

//
//	SetXText
//
//	arguments
//
//		sText	= text
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetXText(CString sText)
{
	m_strXText = sText;
	Redraw();

	return true;
}

//
//	GetXText
//
//	arguments
//
//		none
//
//	return
//
//		sText	= text
//
CString CDataChart::GetXText()
{
	return m_strXText;
}


//
//	SetXLabelStep
//
//	arguments
//
//		nStep = x label step
//
//	return
//
//		true if ok, else false
//
bool CDataChart::SetXLabelStep(int nStep)
{
	m_nXLabelStep = max( nStep, 1 );

	return true;
}

//
//	GetXLabelStep
//
//	arguments
//
//		none
//
//	return
//
//		nStep = x label step
//
int CDataChart::GetXLabelStep()
{
	return m_nXLabelStep;
}


bool CDataChart::CalcLegend()
{
	m_rectLegend.top = m_rectGraph.top;
	m_rectLegend.bottom = m_rectGraph.bottom;
	m_rectLegend.right = m_rectGraph.right;
	m_rectLegend.left = m_rectGraph.right;

	CDataSeries *pdata;
	int maxLegend, widLegend, maxTextWidth, titleWidth;
	CString label;

	if(m_bShowLegend)
	{
		maxLegend = m_rectGraph.Width()*(AREA_LEGEND)/100;

		titleWidth = 20 + 48 + 20;

		maxTextWidth = 0;
		for(int i = 0; i < m_dataset->size(); i++)
		{
			pdata = (CDataSeries *)m_dataset->get(i);
			label = pdata->GetLabel();
			if(label.IsEmpty())
			{
				label.Format("Data Series %d", pdata->GetID());
				pdata->SetLabel(label);
			}

			maxTextWidth = max(maxTextWidth, 5 + 30 + 5 + label.GetLength() * pdata->GetLegendFontSize()/2 + 5);
		}

		widLegend = min(maxLegend, max(titleWidth, maxTextWidth));
		m_rectLegend.left = m_rectLegend.right - widLegend;
	}

	return true;
}

bool CDataChart::DrawLegend(CDC &dc)
{
	if(!m_bShowLegend)
		return true;

	int legendL, legendT, legendR, legendB, legendWidth, nHeight;
	int i, j, legendHeight, maxLegend;
	CDataSeries *pdata;
	CString label;
	CString line;
	TEXTMETRIC tm;
	int *legendLine;


	// Display the rectangle
	CPen pen( PS_SOLID, 1, RGB(0, 0, 0)), *pPenOld;
	pPenOld = dc.SelectObject( &pen );

	legendHeight = 10 + 12 + 5;

	legendLine = CalcLegendLine();
	maxLegend = m_rectGraph.Width()*(AREA_LEGEND)/100;
	for(i = 0; i < m_dataset->size(); i++)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		if(legendLine[i] > 0)
		{
			legendHeight += 5 + pdata->GetLegendFontSize() + 5;
			for(j = 0; j < legendLine[i] - 1; j++)
				legendHeight += pdata->GetLegendFontSize() + 5;
		}
	}

	// rectangle
	legendHeight += 2;

	legendL = m_rectLegend.left + 5;
	legendT = min((int)((double)m_rectUsable.Height() * 0.382), m_rectGraph.top + (m_rectGraph.Height() - legendHeight)/2);
	legendB = legendT + legendHeight;
	legendR = m_rectLegend.right - 5;

	legendWidth = legendR - legendL;

	dc.Rectangle(legendL, legendT, legendR, legendB);
	dc.SelectObject( pPenOld );

	nHeight = 0;

	// Title for "Legend"
	CFont legendTitleFont;
	legendTitleFont.CreateFont(16, 0, 0, 0, 700, FALSE, FALSE, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");
	CFont* pOldFont = (CFont*) dc.SelectObject(&legendTitleFont);
	dc.GetTextMetrics(&tm);
	int charWidth = tm.tmAveCharWidth;
	dc.TextOut(legendL + (legendWidth / 2) - (3 * charWidth), legendT + 5, "Legend");
	dc.SelectObject(pOldFont);

	nHeight += 10 + 12 + 5;

	for(i = 0; i < m_dataset->size(); i++)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		label = pdata->GetLabel();

		// now we draw the short line and markers
		CPen pen( PS_SOLID, pdata->GetSize(), pdata->GetColor() ), *pPenOld;
		CBrush brush( pdata->GetColor() ), *pBrushOld;
		pPenOld = dc.SelectObject( &pen );
		pBrushOld = dc.SelectObject( &brush );

		dc.MoveTo(legendL + 5, legendT + nHeight + 6);
		dc.LineTo(legendL + 5 + 30, legendT + nHeight + 6);

		DrawLinMarker(dc, pdata, legendL + 5 + 15, legendT + nHeight + 6);

		dc.SelectObject( pPenOld );
		dc.SelectObject( pBrushOld );


		// display the text
		CFont legendFont;
		legendFont.CreateFont(pdata->GetLegendFontSize(), 0, 0, 0, 700, FALSE, FALSE, 0,
			ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");
		pOldFont = (CFont*) dc.SelectObject(&legendFont);
		int perLine, lineLoc;

		if(legendLine[i] == 1)
		{
			dc.TextOut(legendL + 5 + 30 + 5, legendT + nHeight, label);
			nHeight += 5 + 12 + 5;
		}
		else if(legendLine[i] > 1)
		{
			//nHeight += 5;
			perLine = 2 * (int)((double)(maxLegend - (5 + 30 + 5 + 5))/(double)pdata->GetLegendFontSize());
			lineLoc = 0;
			for(j = 0; j < legendLine[i]; j++)
			{
				line = label.Mid(lineLoc, perLine);
				lineLoc += line.GetLength();

				// if this is the last line, and there are still some
				// words remained
				if(j == legendLine[i]-1 && lineLoc < label.GetLength())
				{
					line.SetAt(line.GetLength() - 1, '.');
					line.SetAt(line.GetLength() - 2, '.');
					line.SetAt(line.GetLength() - 3, '.');
					line.SetAt(line.GetLength() - 4, ' ');
				}

				dc.TextOut(legendL + 5 + 30 + 5, legendT + nHeight, line);
				nHeight += 12 + 5;
			}
		}

		dc.SelectObject(pOldFont);
	}
	
	return true;
}


void CDataChart::DrawLinMarker(CDC &dc, CDataSeries *ds, int x, int y)
{
	CPen pen( PS_SOLID, ds->GetSize(), ds->GetColor() ), *pPenOld;
	CBrush brush( ds->GetColor() ), *pBrushOld;
	pPenOld = dc.SelectObject( &pen );
	pBrushOld = dc.SelectObject( &brush );
	int m_ratio = ds->GetMarkerRatio();

	POINT* pPoint = new POINT[ 5 ];
	switch( ds->GetMarker() )
	{
		case DATASET_MARKER_TRI:
			pPoint[ 0 ].x = x;
			pPoint[ 0 ].y = y - ds->GetSize()*m_ratio;
			pPoint[ 1 ].x = x + ds->GetSize()*m_ratio;
			pPoint[ 1 ].y = y + ds->GetSize()*m_ratio;
			pPoint[ 2 ].x = x - ds->GetSize()*m_ratio;
			pPoint[ 2 ].y = y + ds->GetSize()*m_ratio;
			dc.Polygon( pPoint, 3 );
			break;

		case DATASET_MARKER_BOX:
			pPoint[ 0 ].x = x - ds->GetSize()*m_ratio;
			pPoint[ 0 ].y = y - ds->GetSize()*m_ratio;
			pPoint[ 1 ].x = x + ds->GetSize()*m_ratio;
			pPoint[ 1 ].y = y - ds->GetSize()*m_ratio;
			pPoint[ 2 ].x = x + ds->GetSize()*m_ratio;
			pPoint[ 2 ].y = y + ds->GetSize()*m_ratio;
			pPoint[ 3 ].x = x - ds->GetSize()*m_ratio;
			pPoint[ 3 ].y = y + ds->GetSize()*m_ratio;
			dc.Polygon( pPoint, 4 );
			break;

		case DATASET_MARKER_SPH:
			pPoint[ 0 ].x = x - ds->GetSize()*m_ratio;
			pPoint[ 0 ].y = y - ds->GetSize()*m_ratio;
			pPoint[ 1 ].x = x + ds->GetSize()*m_ratio;
			pPoint[ 1 ].y = y + ds->GetSize()*m_ratio;
			dc.Ellipse( pPoint[0].x, pPoint[0].y, pPoint[1].x, pPoint[1].y );
			break;

		case DATASET_MARKER_DIA:
			pPoint[ 0 ].x = x;
			pPoint[ 0 ].y = y - ds->GetSize()*m_ratio;
			pPoint[ 1 ].x = x + ds->GetSize()*m_ratio;
			pPoint[ 1 ].y = y;
			pPoint[ 2 ].x = x;
			pPoint[ 2 ].y = y + ds->GetSize()*m_ratio;
			pPoint[ 3 ].x = x - ds->GetSize()*m_ratio;
			pPoint[ 3 ].y = y;
			dc.Polygon( pPoint, 4 );
			break;

		default:
			break;
	}

	delete []pPoint;

	dc.SelectObject( pPenOld );
	dc.SelectObject( pBrushOld );
	return;
}


// Let's figure out how many lines should be displayed for
// each legend.
int * CDataChart::CalcLegendLine()
{
	int *dataLines, *result;
	int i, maxHeight, maxLegend, perLine, textWidth;
	CDataSeries *pdata;
	CString label;
	bool done;

	maxHeight = m_rectData.Height() - 1 - 10 - 12 - 5 -1;
	maxLegend = m_rectGraph.Width()*(AREA_LEGEND)/100;

	dataLines = new int[m_dataset->size()];
	result = new int[m_dataset->size()];

	for(i = 0; i < m_dataset->size(); i++)
	{
		result[i] = 0;

		pdata = (CDataSeries *)m_dataset->get(i);
		label = pdata->GetLabel();

		textWidth = 5 + 30 + 5 + label.GetLength() * pdata->GetLegendFontSize()/2 + 5;
		if(textWidth > maxLegend)
		{
			perLine = 2 * (int)((double)(maxLegend - (5 + 30 + 5 + 5))/(double)pdata->GetLegendFontSize());

			if(label.GetLength() % perLine == 0)
				dataLines[i] = label.GetLength() / perLine;
			else
				dataLines[i] = label.GetLength() / perLine + 1;
		}
		else
			dataLines[i] = 1;
	}


	done = false;
	for(i = 0; i < m_dataset->size(); i++)
	{
		result[i] = 1;
		if(sumLegendHeight(result) > maxHeight)
		{
			result[i] = 0;
			done = true;
			break;
		}

		dataLines[i]--;
	}

	if(allLegendClear(dataLines))
		done = true;

	i = 0;
	while(!done)
	{
		if(i >= m_dataset->size())
		{
			i = 0;
			continue;
		}

		if(dataLines[i] > 0)
		{
			result[i]++;
			if(sumLegendHeight(result) > maxHeight)
			{
				result[i]--;
				done = true;
				break;
			}

			dataLines[i]--;
		}

		if(allLegendClear(dataLines))
			done = true;

		i++;
	}

	delete []dataLines;
	return result;
}


// Check whether all the legend can be shown
bool CDataChart::allLegendClear(int *legendLine)
{
	bool done = true;

	for(int i = 0; i < m_dataset->size(); i++)
	{
		if(legendLine[i] > 0)
			done = false;
	}

	return done;
}

int CDataChart::sumLegendHeight(int *legendLine)
{
	CDataSeries *pdata;
	CString label;
	int totalHeight;

	totalHeight = 0;
	for(int i = 0; i < m_dataset->size(); i++)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		totalHeight += 5 + pdata->GetLegendFontSize() + 5;
		for(int j = 0; j < legendLine[i]-1; j++)
			totalHeight += pdata->GetLegendFontSize() + 5;
	}

	return totalHeight;
}

void CDataChart::DrawPie(CDC &dc)
{
	// Calculate the area for title and pie(s)
	GetClientRect( m_rectArea );

	m_rectUsable.top    = m_rectArea.top    + m_rectArea.Height()/AREA_MARGINS;
	m_rectUsable.bottom = m_rectArea.bottom - m_rectArea.Height()/AREA_MARGINS;
	m_rectUsable.left   = m_rectArea.left   + m_rectArea.Width() /AREA_MARGINS;
	m_rectUsable.right  = m_rectArea.right  - m_rectArea.Width() /AREA_MARGINS;

	// let's calc everything
	if( !m_strTitle.IsEmpty() ) {
		m_rectTitle.top    = m_rectUsable.top;
		m_rectTitle.left   = m_rectUsable.left;
		m_rectTitle.bottom = m_rectUsable.bottom/AREA_TITLE;
		m_rectTitle.right  = m_rectUsable.right;

		m_rectGraph.top    = m_rectTitle.bottom;
		m_rectGraph.left   = m_rectUsable.left;
		m_rectGraph.bottom = m_rectUsable.bottom;
		m_rectGraph.right  = m_rectUsable.right;
	} else {
		m_rectGraph = m_rectUsable;
	}

	PaintBkGnd( dc );

	// display title
	DrawTitle(dc);

	int matrixX, matrixY, pieX, pieY, xDiv, yDiv;
	CDataSeries *pdata;
	CRect rect;

	matrixY = (int)sqrt(m_dataset->size());
	if(m_dataset->size() % matrixY == 0)
		matrixX = m_dataset->size()/matrixY;
	else
		matrixX = m_dataset->size()/matrixY + 1;

	xDiv = m_rectGraph.Width()/matrixX;
	yDiv = m_rectGraph.Height()/matrixY;

	for(int i = 0; i < m_dataset->size(); i++)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		pieX = i % matrixX;
		pieY = i / matrixX;

		rect.top = m_rectGraph.top + pieY*yDiv;
		rect.bottom = rect.top + yDiv;

		rect.left = m_rectGraph.left + pieX*xDiv;
		rect.right = rect.left + xDiv;

		DrawPieDataset(dc, pdata, rect);
	}
}

void CDataChart::DrawPieDataset(CDC &dc, CDataSeries *ds, CRect rectArea)
{
	double *Slices, total, yValue, dataSum, degree, labelDegree;
	int i, xValue, radius, clrRound;
	int lastXLocation, lastYLocation, newXLocation, newYLocation, centreX, centreY;
	int labelX, labelY;
	CRect pieRect;
	CRect titleRect;
	CRect labelRect;

	COLORREF barColor;
	CPen* pOldPen;
	CBrush* pOldBrush;

	CString label;
	int numLess;


	// Normalization
	total = 0;
	Slices = (double *) new double[ds->GetDatasetSize()];
	for(i = 0; i < ds->GetDatasetSize(); i++)
	{
		ds->Get(i, xValue, yValue);
		Slices[i] = yValue;
		total += yValue;
	}

	for(i = 0; i < ds->GetDatasetSize(); i++)
		Slices[i] /= total;

	// draw title
	titleRect.bottom = rectArea.bottom;
	titleRect.right = rectArea.right;
	titleRect.left = rectArea.left;
	titleRect.top = rectArea.bottom - 2 - 12 - 2;


	//draw normal pie
	centreX = rectArea.left + rectArea.Width()/2;
	centreY = rectArea.top + (rectArea.Height()-titleRect.Height())/2;

	radius = (min(rectArea.Height()-titleRect.Height(), rectArea.Width()) - 60)/2;


	titleRect.top = min(titleRect.top, centreY + radius + 2 + 12 + 2);
	DrawText(dc, ds->GetLabel(), titleRect, 12, DT_CENTER);


	lastXLocation = centreX + radius;
	lastYLocation = centreY;

	pieRect.top = centreY - radius;
	pieRect.left = centreX - radius;
	pieRect.bottom = centreY + radius;
	pieRect.right = centreX + radius;

	dataSum = 0.0;
	clrRound = 0;
	numLess = 0;
	for(i = 0; i < ds->GetDatasetSize(); i++)
	{
		ds->Get(i, xValue, yValue);

		if(Slices[i] < 3.1415926535/720.0)
		{
			numLess++;
			continue;
		}

		labelDegree = (dataSum + Slices[i]/2.0)*2.0*3.1415926535;

		dataSum += Slices[i];
		degree = dataSum*2.0*3.1415926535;

		newXLocation = centreX + (int)((double)radius*cos(degree));
		newYLocation = centreY - (int)((double)radius*sin(degree));

		if(clrRound >= COLOR_NUM)
			clrRound = 0;

		barColor = clrList[clrRound];
		clrRound++;

		CPoint p1 (lastXLocation, lastYLocation);
		CPoint p2 (newXLocation, newYLocation);
		CBrush brush (barColor);
		CPen piePen (PS_SOLID, 1, barColor);
		pOldPen = dc.SelectObject(&piePen);
		pOldBrush = dc.SelectObject(&brush);

		dc.Pie(pieRect, p1, p2); 

		dc.SelectObject(pOldBrush);
		dc.SelectObject(pOldPen);


		// Now we draw the label for each data slice.
		labelX = centreX + (int)((double)radius*cos(labelDegree));
		labelY = centreY - (int)((double)radius*sin(labelDegree));

		label.Format("%d", xValue);
		if(labelX >= centreX && labelY <= centreY)
		{
			// first Quadrant
			labelRect.right = rectArea.right;
			labelRect.bottom = labelY;
			labelRect.top = labelY - 12;
			labelRect.left = labelX;

			DrawText(dc, label, labelRect, 12, DT_LEFT);
		}
		else if(labelX < centreX && labelY <= centreY)
		{
			// second Quadrant
			labelRect.right = labelX;
			labelRect.bottom = labelY;
			labelRect.top = labelY - 12;
			labelRect.left = rectArea.left;

			DrawText(dc, label, labelRect, 12, DT_RIGHT);
		}
		else if(labelX < centreX && labelY > centreY)
		{
			// third Quadrant
			labelRect.left = rectArea.left;
			labelRect.right = labelX;
			labelRect.top = labelY;
			labelRect.bottom = labelY + 12;

			DrawText(dc, label, labelRect, 12, DT_RIGHT);
		}
		else if(labelX >= centreX && labelY > centreY)
		{
			// fourth Quadrant
			labelRect.left = labelX;
			labelRect.right = rectArea.right;
			labelRect.top = labelY;
			labelRect.bottom = labelY + 12;

			DrawText(dc, label, labelRect, 12, DT_LEFT);
		}

		lastXLocation = newXLocation;
		lastYLocation = newYLocation;
	}

	if(lastXLocation != centreX + radius && lastYLocation != centreY)
	{
		CPoint p1 (lastXLocation, lastYLocation);
		CPoint p2 (centreX + radius, centreY);
		CBrush brush (BLACK);
		CPen piePen (PS_SOLID, 1, BLACK);
		pOldPen = dc.SelectObject(&piePen);
		pOldBrush = dc.SelectObject(&brush);

		dc.Pie(pieRect, p1, p2); 

		dc.SelectObject(pOldBrush);
		dc.SelectObject(pOldPen);

		labelRect.left = centreX + radius;
		labelRect.right = rectArea.right;
		labelRect.top = centreY;
		labelRect.bottom = centreY + 12;

		label.Format("%ds'", numLess);

		DrawText(dc, label, labelRect, 12, DT_LEFT);
	}



	delete Slices;
}

void CDataChart::DrawText(CDC &dc, CString textValue, CRect rectArea, int fontSize, UINT format)
{
	CString textBuf;
	const int nBkModeOld = dc.SetBkMode( TRANSPARENT );
	COLORREF clrBlack = RGB(   0,   0,   0), clrOld;
	CPen textPen(PS_SOLID, 3, clrBlack ), *pPenOld;
	CFont textFont, *pFontOld;
	int maxWord;

	maxWord = rectArea.Width()/(fontSize/2 - 1);

	textFont.CreateFont( fontSize, 0, 0, 0, FW_NORMAL,
					 FALSE, FALSE, FALSE, ANSI_CHARSET,
					 OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY,
					 DEFAULT_PITCH, "Arial");

	pPenOld  = dc.SelectObject(&textPen);
	pFontOld = dc.SelectObject(&textFont);
	clrOld   = dc.SetTextColor( clrBlack );

	if(textValue.GetLength() > maxWord)
	{
		textBuf = textValue.Left(maxWord);
		if(textBuf.GetLength() > 10)
		{
			textBuf.SetAt(maxWord-1, '.');
			textBuf.SetAt(maxWord-2, '.');
			textBuf.SetAt(maxWord-3, '.');
			textBuf.SetAt(maxWord-4, ' ');
		}
		else
		{
			textBuf.SetAt(maxWord-1, '.');
		}
	}
	else
		textBuf = textValue;

	dc.DrawText( textBuf, rectArea, format | DT_TOP | DT_SINGLELINE );

	dc.SelectObject(pPenOld);
	dc.SelectObject(pFontOld);
	dc.SetTextColor(clrOld);
	dc.SetBkMode(nBkModeOld);

	return;
}

void CDataChart::DrawLine(CDC &dc)
{
	CDataSeries *pdata;
	CPen *pPenOld;

	PaintBkGnd( dc );
	DrawTitle( dc );


	// Draw Axes
	// 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 );

	if(m_bShowYScale)
		DrawYScale( dc );

	if(m_bShowXScale)
		DrawXScale( dc );

	// Draw Grid
	if(m_bShowGrid)
	{
		DrawHorzLine( dc );
		DrawVertLine( dc );
	}

	// Draw Base line
	if( m_nYMin < 0 && m_bShowBaseLine)
	{
		CPen baseLinePen( PS_SOLID, 1, RGB(0,0,0));
		double nTemp = ( - m_nYMin) * m_rectData.Height()/(m_nYMax-m_nYMin); // this is the zero baseline

		pPenOld = dc.SelectObject( &baseLinePen );

		dc.MoveTo( m_rectData.left , m_rectData.bottom - (int)nTemp );
		dc.LineTo( m_rectData.right, m_rectData.bottom - (int)nTemp );

		dc.SelectObject( pPenOld );
	}


	// Draw dataset from last to first so I can show 
	// first dataset in foreground, below the second dataset and so on
	for( int i = m_dataset->size() - 1; i>=0; i--)
	{
		pdata = (CDataSeries *)m_dataset->get(i);
		DrawDataset( dc, pdata );
	}

	if(m_bShowLegend)
		DrawLegend(dc);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Researcher
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions