Click here to Skip to main content
15,885,914 members
Articles / Desktop Programming / MFC

2D Animated Charts

Rate me:
Please Sign up or sign in to vote.
3.73/5 (35 votes)
24 Jan 2005CPOL6 min read 236.2K   9.5K   114  
An article on creating 2D animated charts using Windows GDI.
// 2DLineGraph.cpp: implementation of the C2DLineGraph class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "2DLineGraph.h"

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

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

C2DLineGraph::C2DLineGraph(CPoint g_position, CSize g_size)
{
	// Set graph position and size
	m_Position = g_position;
	m_Size = g_size;

	// Set graph segments and series number
	m_SegmentsNumber = 0;
	m_Segments = NULL;
	m_SeriesNumber = 0;
	m_Series = NULL;

	// Set graph legend font
	m_LegendFont = new CFont();
	m_LegendFont->CreateFont( int(m_Size.cy*0.075), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
		DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Times New Roman" );

	// Set graph series font
	m_SegmentFont = NULL;

	// Set graph value font
	m_ValueFont = new CFont();
	m_ValueFont->CreateFont( int(m_Size.cy*0.1), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
		DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );

	// Set default graph background color
	m_GraphBkColor = RGB(255,255,2555);

	// Set default graph labels color
	m_LabelsColor = RGB(0,0,0);

	// Set default graph legend background color
	m_LegendBackgroundColor = RGB(255,255,255);
	m_LegendTextColor = RGB(0,0,0);

	// Set default graph draw series points
	m_DrawSeriesPoints = NULL;
}

C2DLineGraph::~C2DLineGraph()
{
	// Delete graph segments and series
	ClearGraph();

	// Delete graph legend font
	m_LegendFont->DeleteObject();
	delete m_LegendFont;
	m_LegendFont = NULL;

	// Delete graph segment font
	m_SegmentFont = NULL;

	// Delete graph value font
	m_ValueFont->DeleteObject();
	delete m_ValueFont;
	m_ValueFont = NULL;

	// Delete series points
	delete []m_DrawSeriesPoints;
}

void C2DLineGraph::ClearGraph()
{
	_2DLineGraphSeries *curr_ser, *prev_ser;
	_2DLineGraphSegments *curr_seg, *prev_seg;
	_2DLineGraphValues *curr_val, *prev_val;

	// Delete all graph series
	curr_ser = m_Series;
	while ( curr_ser != NULL )
	{
		// Delete all graph values
		curr_val = curr_ser->values;
		while ( curr_val != NULL )
		{
			prev_val = curr_val;
			curr_val = curr_val->next;
			delete prev_val;
		}

		prev_ser = curr_ser;
		curr_ser = curr_ser->next;
		delete prev_ser;
	}

	// Delete all graph segments
	curr_seg = m_Segments;
	while ( curr_seg != NULL )
	{
		prev_seg = curr_seg;
		curr_seg = curr_seg->next;
		delete prev_seg;
	}

	// Set graph segment and series parameters
	m_Segments = NULL;
	m_SegmentsNumber = 0;
	m_Series = NULL;
	m_SeriesNumber = 0;
}

void C2DLineGraph::BuildGraph(HDC hDC)
{
	int seg_width, seg_height;
	CPoint ser_start, ser_end, val_start, val_end, seg_font_pos;
	int ser_width, ser_region_width, ser_region_start;
	int max_value;
	double dist, angle;
	int seg_perc;
	bool exit, flag;

	// Set graph step parameters
	int seg_area = int(m_Size.cx*0.65);
	int seg_step = int(double(seg_area)/(4*double(m_SegmentsNumber)));
	int ser_step = int(double(seg_step)/8);

	// Set segment font height
	int seg_number = m_SegmentsNumber;
	if ( seg_number == 1 )
		seg_number++;
	int font_height = int((double(seg_area)/double(seg_number))*0.38 + 0.5);

	_2DLineGraphSegments *curr_seg, *next_seg;
	_2DLineGraphSeries *c_ser, *curr_ser;
	_2DLineGraphValues *c_val, *curr_val, *next_val;

	HFONT hOldSegFont, hOldValFont;

	// Calculate graph x and y coordinate system begining
	int startX = m_Position.x + int( m_Size.cx*0.15 );
	int startY = m_Position.y + int( m_Size.cy*0.95 );
	// Draw x coordinate axis
	::MoveToEx( hDC, startX, startY, NULL );
	::LineTo( hDC, m_Size.cx, startY );
	// Draw x coordinate axis arrow
	::MoveToEx( hDC, m_Size.cx, startY, NULL );
	::LineTo( hDC, int(m_Size.cx*0.98), startY-int(m_Size.cy*0.02) );
	::MoveToEx( hDC, m_Size.cx, startY, NULL );
	::LineTo( hDC, int(m_Size.cx*0.98), startY+int(m_Size.cy*0.02) );
	// Draw y coordinate axis
	::MoveToEx( hDC, startX, startY, NULL );
	::LineTo( hDC, startX, m_Position.y + int(m_Size.cy*0.05) );
	// Draw y coordinate axis arrow
	::MoveToEx( hDC, startX, m_Position.y + int(m_Size.cy*0.05), NULL );
	::LineTo( hDC, startX - int(m_Size.cx*0.015), m_Position.y + int(m_Size.cy*0.085) );
	::MoveToEx( hDC, startX, m_Position.y + int(m_Size.cy*0.05), NULL );
	::LineTo( hDC, startX + int(m_Size.cx*0.015), m_Position.y + int(m_Size.cy*0.085) );

	// If animation running
	if ( m_Animation == TRUE )
	{
		// If draw-all animation running
		if ( m_AnimationType == AT_LINE_DRAW_ALL )
		{
			// Check segments number
			if ( ( m_SegmentsNumber > 1 ) && ( m_SeriesNumber > 0 ) )
			{
				// Calculate segments width
				seg_width = int((double(seg_area) - (m_SegmentsNumber-1)*seg_step + m_SegmentsNumber*(m_SeriesNumber-1)*ser_step)/double(m_SegmentsNumber));

				// Calculate series start point and width
				if ( m_SegmentsNumber > 1 )
					ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
				else
					ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
				seg_font_pos = ser_start;
				ser_width = int((double(seg_width) - (m_SeriesNumber-1)*ser_step)/double(m_SeriesNumber));
				ser_region_width = int(m_Size.cy*0.8);
				ser_region_start = m_Position.y + int(m_Size.cy*0.93);

				// Get max series value
				max_value = 0;
				c_ser = m_Series;
				c_val = NULL;
				curr_seg = m_Segments;
				while ( curr_seg != NULL )
				{
					while ( c_ser != NULL )
					{
						// Find segment value
						c_val = c_ser->values;
						while ( c_val->index != curr_seg->index )
							c_val = c_val->next;
						// Check max value
						if ( c_val->value > max_value )
							max_value = c_val->value;

						// Get next series
						c_ser = c_ser->next;
					}

					// Get next segment
					curr_seg = curr_seg->next;
				}

				// Draw graph values text
				hOldValFont = (HFONT)::SelectObject( hDC, m_ValueFont->GetSafeHandle() );
				::SetBkMode( hDC, TRANSPARENT );
				::SetTextAlign( hDC, TA_RIGHT );
				COLORREF oldTxtColor = ::SetTextColor( hDC, m_LabelsColor );
				CString str_max;
				str_max.Format( "%d", max_value );
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.85), str_max, str_max.GetLength() );
				str_max.Format( "%d", int(double(max_value)/2.0) );
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.45), str_max, str_max.GetLength() );
				str_max = "0";
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.05), str_max, str_max.GetLength() );
				::SetTextAlign( hDC, TA_LEFT );
				::SetBkMode( hDC, OPAQUE );
				::SelectObject( hDC, hOldValFont );
				::SetTextColor( hDC, oldTxtColor );

				// Draw grap segments
				exit = false;
				curr_seg = m_Segments;
				while ( curr_seg != NULL )
				{
					// Draw graph segments text
					COLORREF oldTextColor = ::SetTextColor( hDC, m_LabelsColor );
					// Set new graph legend font
					m_SegmentFont = new CFont();
					m_SegmentFont->CreateFont( font_height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
						DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );
					// Draw segment text
					hOldSegFont = (HFONT)::SelectObject( hDC, m_SegmentFont->GetSafeHandle() );
					::SetBkMode( hDC, TRANSPARENT );
					::SetTextAlign( hDC, TA_CENTER );
					::TextOut( hDC, seg_font_pos.x + int((seg_width + m_SeriesNumber*ser_step)/2), startY+int(m_Size.cy*0.01), curr_seg->text, curr_seg->text.GetLength() );
					::SetTextAlign( hDC, TA_LEFT );
					::SetBkMode( hDC, OPAQUE );
					::SelectObject( hDC, hOldSegFont );
					seg_font_pos.x += seg_width + seg_step;
					::SetTextColor( hDC, oldTextColor );
					m_SegmentFont->DeleteObject();
					delete m_SegmentFont;

					// Draw graph series
					curr_ser = m_Series;
					curr_val = next_val = NULL;
					while ( curr_ser != NULL )
					{
						// Draw graph series values
						curr_val = curr_ser->values;
						next_val = curr_val->next;

						// Calculate series start point and width
						if ( m_SegmentsNumber > 1 )
							ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
						else
							ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );

						// If animating current segment
						if ( curr_seg->index > m_DrawSegmentsCompleted )
						{
							while ( curr_val->index < curr_seg->index )
								curr_val = curr_val->next;
							next_val = curr_val->next;

							// Check next series value
							while ( next_val != NULL )
							{
								// Calculate value start and end point
								ser_end.x = m_DrawSeriesPoints[curr_ser->index-1].x + seg_width + seg_step;
								if ( curr_val->index == 1 )
								{
									val_start.x = m_DrawSeriesPoints[curr_ser->index-1].x + int(seg_width/2);
									seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
									val_start.y = ser_region_start - seg_height;
								}
								else
									val_start = m_DrawSeriesPoints[curr_ser->index-1];

								if ( curr_val->index == 1 )
									val_end.x = ser_end.x + int(seg_width/2);
								else
									val_end.x = ser_end.x;
								seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
								val_end.y = ser_region_start - seg_height;

								seg_perc = m_AnimationPercent - m_DrawSegmentsCompleted*m_SegmentsPercent;
								dist = (double(seg_perc)/double(m_SegmentsPercent))*sqrt(pow((val_start.x-val_end.x),2)+pow((val_start.y-val_end.y),2));
								angle = atan(double(val_end.y-val_start.y)/double(val_end.x-val_start.x));
								val_end.x = val_start.x + int(dist*cos(angle));
								val_end.y = val_start.y + int(dist*sin(angle));

								// Draw value line
								LOGBRUSH lb;
								lb.lbStyle = BS_SOLID;
								lb.lbColor = curr_ser->color;
								HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
								HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
								::MoveToEx( hDC, val_start.x, val_start.y, NULL );
								::LineTo( hDC, val_end.x, val_end.y );
								::SelectObject( hDC, hOldPen );
								::DeleteObject( hPen );

								// Exit animation loop
								exit = true;
								break;

								// Get next value
								curr_val = next_val;
								next_val = next_val->next;
							}
						}
						// If current segment animation finished
						else
						{
							// Check next series value
							while ( ( next_val != NULL ) && ( (next_val->index-1) <= m_DrawSegmentsCompleted ) )
							{
								// Calculate value start and end point
								ser_end.x = ser_start.x + seg_width + seg_step;
								if ( curr_val->index == 1 )
								{
									val_start.x = ser_start.x + int(seg_width/2);
									seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
									val_start.y = ser_region_start - seg_height;
								}
								else
									val_start = val_end;
								val_end.x = ser_end.x + int(seg_width/2);
								seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
								val_end.y = ser_region_start - seg_height;

								// Draw value line
								LOGBRUSH lb;
								lb.lbStyle = BS_SOLID;
								lb.lbColor = curr_ser->color;
								HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
								HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
								::MoveToEx( hDC, val_start.x, val_start.y, NULL );
								::LineTo( hDC, val_end.x, val_end.y );
								::SelectObject( hDC, hOldPen );
								::DeleteObject( hPen );

								// Set next series start point
								ser_start.x += seg_width + seg_step;

								// Get next value
								curr_val = next_val;
								next_val = next_val->next;
							}

							// Set new series start point
							m_DrawSeriesPoints[curr_ser->index-1] = val_end;
						}

						// Get next series
						curr_ser = curr_ser->next;
					}

					// Exit animation loop
					if ( exit == true )
						break;

					// Get next segment
					curr_seg = curr_seg->next;
				}
			}
		}
		// If draw-series animation running
		else if ( m_AnimationType == AT_LINE_DRAW_SERIES )
		{
			// Check segments number
			if ( ( m_SegmentsNumber > 1 ) && ( m_SeriesNumber > 0 ) )
			{
				// Calculate segments width
				seg_width = int((double(seg_area) - (m_SegmentsNumber-1)*seg_step + m_SegmentsNumber*(m_SeriesNumber-1)*ser_step)/double(m_SegmentsNumber));

				// Calculate series start point and width
				if ( m_SegmentsNumber > 1 )
					ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
				else
					ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
				seg_font_pos = ser_start;
				ser_width = int((double(seg_width) - (m_SeriesNumber-1)*ser_step)/double(m_SeriesNumber));
				ser_region_width = int(m_Size.cy*0.8);
				ser_region_start = m_Position.y + int(m_Size.cy*0.93);

				// Get max series value
				max_value = 0;
				c_ser = m_Series;
				c_val = NULL;
				curr_seg = m_Segments;
				while ( curr_seg != NULL )
				{
					while ( c_ser != NULL )
					{
						// Find segment value
						c_val = c_ser->values;
						while ( c_val->index != curr_seg->index )
							c_val = c_val->next;
						// Check max value
						if ( c_val->value > max_value )
							max_value = c_val->value;

						// Get next series
						c_ser = c_ser->next;
					}

					// Get next segment
					curr_seg = curr_seg->next;
				}

				// Draw graph values text
				hOldValFont = (HFONT)::SelectObject( hDC, m_ValueFont->GetSafeHandle() );
				::SetBkMode( hDC, TRANSPARENT );
				::SetTextAlign( hDC, TA_RIGHT );
				COLORREF oldTextColor = ::SetTextColor( hDC, m_LabelsColor );
				CString str_max;
				str_max.Format( "%d", max_value );
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.85), str_max, str_max.GetLength() );
				str_max.Format( "%d", int(double(max_value)/2.0) );
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.45), str_max, str_max.GetLength() );
				str_max = "0";
				::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.05), str_max, str_max.GetLength() );
				::SetTextAlign( hDC, TA_LEFT );
				::SetBkMode( hDC, OPAQUE );
				::SelectObject( hDC, hOldValFont );

				// Draw graph segments text
				curr_seg = m_Segments;
				while ( curr_seg != NULL )
				{
					// Set new graph legend font
					m_SegmentFont = new CFont();
					m_SegmentFont->CreateFont( font_height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
						DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );
					// Draw segment text
					hOldSegFont = (HFONT)::SelectObject( hDC, m_SegmentFont->GetSafeHandle() );
					::SetBkMode( hDC, TRANSPARENT );
					::SetTextAlign( hDC, TA_CENTER );
					::TextOut( hDC, seg_font_pos.x + int((seg_width + m_SeriesNumber*ser_step)/2), startY+int(m_Size.cy*0.01), curr_seg->text, curr_seg->text.GetLength() );
					::SetTextAlign( hDC, TA_LEFT );
					::SetBkMode( hDC, OPAQUE );
					::SelectObject( hDC, hOldSegFont );
					m_SegmentFont->DeleteObject();
					delete m_SegmentFont;

					curr_seg = curr_seg->next;
					seg_font_pos.x += seg_width + seg_step;
				}
				::SetTextColor( hDC, oldTextColor );

				// Draw grap segments
				exit = false;
				flag = false;
				curr_seg = m_Segments;
				// Draw graph series
				curr_ser = m_Series;
				curr_val = next_val = NULL;
				while ( curr_seg != NULL )
				{
					// If animating current series
					if ( curr_ser->index > m_DrawSeriesCompleted )
					{
						// Draw graph series values
						curr_val = curr_ser->values;
						next_val = curr_val->next;

						// Calculate series start point and width
						if ( m_SegmentsNumber > 1 )
							ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
						else
							ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );

						// If animating current segment
						if ( curr_seg->index > m_DrawSegmentsCompleted )
						{
							while ( curr_val->index < curr_seg->index )
								curr_val = curr_val->next;
							next_val = curr_val->next;

							// Check next series value
							while ( next_val != NULL )
							{
								// Calculate value start and end point
								ser_end.x = m_DrawSeriesPoints[curr_ser->index-1].x + seg_width + seg_step;
								if ( curr_val->index == 1 )
								{
									val_start.x = m_DrawSeriesPoints[curr_ser->index-1].x + int(seg_width/2);
									seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
									val_start.y = ser_region_start - seg_height;
								}
								else
									val_start = m_DrawSeriesPoints[curr_ser->index-1];

								if ( curr_val->index == 1 )
									val_end.x = ser_end.x + int(seg_width/2);
								else
									val_end.x = ser_end.x;
								seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
								val_end.y = ser_region_start - seg_height;

								seg_perc = m_AnimationPercent - m_DrawSegmentsCompleted*m_SegmentsPercent;
								dist = (double(seg_perc)/double(m_SegmentsPercent))*sqrt(pow((val_start.x-val_end.x),2)+pow((val_start.y-val_end.y),2));
								angle = atan(double(val_end.y-val_start.y)/double(val_end.x-val_start.x));
								val_end.x = val_start.x + int(dist*cos(angle));
								val_end.y = val_start.y + int(dist*sin(angle));

								// Draw value line
								LOGBRUSH lb;
								lb.lbStyle = BS_SOLID;
								lb.lbColor = curr_ser->color;
								HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
								HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
								::MoveToEx( hDC, val_start.x, val_start.y, NULL );
								::LineTo( hDC, val_end.x, val_end.y );
								::SelectObject( hDC, hOldPen );
								::DeleteObject( hPen );

								// Exit animation loop
								exit = true;
								break;

								// Get next value
								curr_val = next_val;
								next_val = next_val->next;
							}

							flag = true;
						}
						// If current segment animation finished
						else
						{
							// Check next series value
							while ( ( next_val != NULL ) && ( (next_val->index-1) <= m_DrawSegmentsCompleted ) )
							{
								// Calculate value start and end point
								ser_end.x = ser_start.x + seg_width + seg_step;
								if ( curr_val->index == 1 )
								{
									val_start.x = ser_start.x + int(seg_width/2);
									seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
									val_start.y = ser_region_start - seg_height;
								}
								else
									val_start = val_end;
								val_end.x = ser_end.x + int(seg_width/2);
								seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
								val_end.y = ser_region_start - seg_height;

								// Draw value line
								LOGBRUSH lb;
								lb.lbStyle = BS_SOLID;
								lb.lbColor = curr_ser->color;
								HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
								HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
								::MoveToEx( hDC, val_start.x, val_start.y, NULL );
								::LineTo( hDC, val_end.x, val_end.y );
								::SelectObject( hDC, hOldPen );
								::DeleteObject( hPen );

								// Set next series start point
								ser_start.x += seg_width + seg_step;

								// Get next value
								curr_val = next_val;
								next_val = next_val->next;
							}

							// Set new series start point
							m_DrawSeriesPoints[curr_ser->index-1] = val_end;

							flag = false;
						}
					}
					// If current series animation finished
					else
					{
						// Draw graph series values
						curr_val = curr_ser->values;
						next_val = curr_val->next;

						// Calculate series start point and width
						if ( m_SegmentsNumber > 1 )
							ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
						else
							ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );

						// Check next series value
						while ( next_val != NULL )
						{
							// Calculate value start and end point
							ser_end.x = ser_start.x + seg_width + seg_step;
							if ( curr_val->index == 1 )
							{
								val_start.x = ser_start.x + int(seg_width/2);
								seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
								val_start.y = ser_region_start - seg_height;
							}
							else
								val_start = val_end;
							val_end.x = ser_end.x + int(seg_width/2);
							seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
							val_end.y = ser_region_start - seg_height;

							// Draw value line
							LOGBRUSH lb;
							lb.lbStyle = BS_SOLID;
							lb.lbColor = curr_ser->color;
							HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
							HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
							::MoveToEx( hDC, val_start.x, val_start.y, NULL );
							::LineTo( hDC, val_end.x, val_end.y );
							::SelectObject( hDC, hOldPen );
							::DeleteObject( hPen );

							// Set next series start point
							ser_start.x += seg_width + seg_step;

							// Get next value
							curr_val = next_val;
							next_val = next_val->next;
						}

						// Get next series
						if ( curr_ser->next != NULL )
							curr_ser = curr_ser->next;
						else
							exit = true;

						flag = true;
					}

					// Exit animation loop
					if ( exit == true )
						break;

					if ( flag == false )
					{
						// Get next segment
						curr_seg = curr_seg->next;
					}
				}
			}
		}
	}
	// If no animation running
	else if ( m_Animation == FALSE )
	{
		// Check segments number
		if ( ( m_SegmentsNumber > 1 ) && ( m_SeriesNumber > 0 ) )
		{
			// Calculate segments width
			seg_width = int((double(seg_area) - (m_SegmentsNumber-1)*seg_step + m_SegmentsNumber*(m_SeriesNumber-1)*ser_step)/double(m_SegmentsNumber));

			// Calculate series start point and width
			if ( m_SegmentsNumber > 1 )
				ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
			else
				ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
			seg_font_pos = ser_start;
			ser_width = int((double(seg_width) - (m_SeriesNumber-1)*ser_step)/double(m_SeriesNumber));
			ser_region_width = int(m_Size.cy*0.8);
			ser_region_start = m_Position.y + int(m_Size.cy*0.93);

			// Get max series value
			max_value = 0;
			c_ser = m_Series;
			c_val = NULL;
			curr_seg = m_Segments;
			while ( curr_seg != NULL )
			{
				while ( c_ser != NULL )
				{
					// Find segment value
					c_val = c_ser->values;
					while ( c_val->index != curr_seg->index )
						c_val = c_val->next;
					// Check max value
					if ( c_val->value > max_value )
						max_value = c_val->value;

					// Get next series
					c_ser = c_ser->next;
				}

				// Get next segment
				curr_seg = curr_seg->next;
			}

			// Draw graph values text
			hOldValFont = (HFONT)::SelectObject( hDC, m_ValueFont->GetSafeHandle() );
			::SetBkMode( hDC, TRANSPARENT );
			::SetTextAlign( hDC, TA_RIGHT );
			COLORREF oldTextColor = ::SetTextColor( hDC, m_LabelsColor );
			CString str_max;
			str_max.Format( "%d", max_value );
			::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.85), str_max, str_max.GetLength() );
			str_max.Format( "%d", int(double(max_value)/2.0) );
			::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.45), str_max, str_max.GetLength() );
			str_max = "0";
			::TextOut( hDC, startX-int(m_Size.cx*0.02), ser_start.y-int(m_Size.cy*0.05), str_max, str_max.GetLength() );
			::SetTextAlign( hDC, TA_LEFT );
			::SetBkMode( hDC, OPAQUE );
			::SelectObject( hDC, hOldValFont );

			// Draw graph segments text
			curr_seg = m_Segments;
			next_seg = curr_seg->next;
			while ( curr_seg != NULL )
			{
				// Set new graph legend font
				m_SegmentFont = new CFont();
				m_SegmentFont->CreateFont( font_height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
					DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );
				// Draw segment text
				hOldSegFont = (HFONT)::SelectObject( hDC, m_SegmentFont->GetSafeHandle() );
				::SetBkMode( hDC, TRANSPARENT );
				::SetTextAlign( hDC, TA_CENTER );
				::TextOut( hDC, seg_font_pos.x + int((seg_width + m_SeriesNumber*ser_step)/2), startY+int(m_Size.cy*0.01), curr_seg->text, curr_seg->text.GetLength() );
				::SetTextAlign( hDC, TA_LEFT );
				::SetBkMode( hDC, OPAQUE );
				::SelectObject( hDC, hOldSegFont );
				m_SegmentFont->DeleteObject();
				delete m_SegmentFont;

				seg_font_pos.x += seg_width + seg_step;

				// Get next segment
				curr_seg = curr_seg->next;
			}
			::SetTextColor( hDC, oldTextColor );

			// Draw graph series
			curr_ser = m_Series;
			curr_val = next_val = NULL;
			while ( curr_ser != NULL )
			{
				// Draw graph series values
				curr_val = curr_ser->values;
				next_val = curr_val->next;

				// Set series start point
				if ( m_SegmentsNumber > 1 )
					ser_start = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
				else
					ser_start = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );

				// Check next series value
				while ( next_val != NULL )
				{
					// Calculate value start and end point
					ser_end.x = ser_start.x + seg_width + seg_step;
					if ( curr_val->index == 1 )
					{
						val_start.x = ser_start.x + int(seg_width/2);
						seg_height = int((double(curr_val->value)/double(max_value)) * ser_region_width);
						val_start.y = ser_region_start - seg_height;
					}
					else
						val_start = val_end;
					val_end.x = ser_end.x + int(seg_width/2);
					seg_height = int((double(next_val->value)/double(max_value)) * ser_region_width);
					val_end.y = ser_region_start - seg_height;

					// Draw value line
					LOGBRUSH lb;
					lb.lbStyle = BS_SOLID;
					lb.lbColor = curr_ser->color;
					HPEN hPen = ::ExtCreatePen( PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3, &lb, 0, NULL );
					HPEN hOldPen = (HPEN)::SelectObject( hDC, hPen );
					::MoveToEx( hDC, val_start.x, val_start.y, NULL );
					::LineTo( hDC, val_end.x, val_end.y );
					::SelectObject( hDC, hOldPen );
					::DeleteObject( hPen );

					// Set next series start point
					ser_start.x += seg_width + seg_step;

					// Get next value
					curr_val = next_val;
					next_val = next_val->next;
				}

				// Get next series
				curr_ser = curr_ser->next;
			}
		}
	}
}

void C2DLineGraph::BuildGraphLegend(HDC hDC)
{
	_2DLineGraphSeries *curr_ser = m_Series;

	// Select legend font
	HGDIOBJ hOldFont = ::SelectObject( hDC, m_LegendFont->GetSafeHandle() );

	// Get text size
	CSize ts;
	::GetTextExtentPoint32( hDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, &ts );

	int offset = (m_Size.cy - int(m_SeriesNumber*ts.cy))/2;

	CPoint start = CPoint( m_Position.x + int(m_Size.cx*0.97), m_Position.y + int(m_Size.cy*0.014) + offset );
	CSize sz;
	CString str = "";
	CRect rect;
	CBrush* rBrush = NULL;

	// Draw graph legend
	CBrush bgBrush( m_LegendBackgroundColor );
	HBRUSH hOldBrush = (HBRUSH)::SelectObject( hDC, bgBrush.GetSafeHandle() );
	::Rectangle( hDC, m_Position.x + int(m_Size.cx*0.95), m_Position.y + offset, m_Position.x + int(m_Size.cx*1.25), m_Position.y + int((m_SeriesNumber+0.5)*ts.cy + offset) );
	::SelectObject( hDC, hOldBrush );
	bgBrush.DeleteObject();

	COLORREF oldTextColor = ::SetTextColor( hDC, m_LegendTextColor );
	while ( curr_ser != NULL )
	{
		// Draw segment color field
		rect = CRect( start.x, start.y + int(m_Size.cy*0.03)/2, start.x + int(m_Size.cx*0.05), start.y + int(m_Size.cy*0.075) );
		rBrush = new CBrush();
		rBrush->CreateSolidBrush( curr_ser->color );
		::FillRect( hDC, rect, (HBRUSH)rBrush->GetSafeHandle() );
		rBrush->DeleteObject();
		delete rBrush;

		// Draw segment text
		str.Format( "%s", curr_ser->text );
		::SetBkMode( hDC, TRANSPARENT );
		::TextOut( hDC, start.x + rect.Width() + int(m_Size.cx*0.01), start.y, str, str.GetLength() );
		::SetBkMode( hDC, OPAQUE );

		// Recalculate next text position
		::GetTextExtentPoint32( hDC, curr_ser->text, curr_ser->text.GetLength(), &sz );
		start.y += sz.cy;

		// Get next graph segment
		curr_ser = curr_ser->next;
	}
	::SetTextColor( hDC, oldTextColor );

	// Select old font
	::SelectObject( hDC, hOldFont );
}

BOOL C2DLineGraph::GetGraphAnimation()
{
	return m_Animation;
}

int C2DLineGraph::GetGraphAnimationPercent()
{
	return m_AnimationPercent;
}

void C2DLineGraph::SetGraphBkColor(COLORREF g_bkColor)
{
	// Set graph background color
	m_GraphBkColor = g_bkColor;
}

void C2DLineGraph::SetGraphPosition(CPoint g_position)
{
	// Set new graph position
	m_Position = g_position;
}

void C2DLineGraph::SetGraphSize(CSize g_size)
{
	// Set new graph size
	m_Size = g_size;

	// Set new graph legend font size
	m_LegendFont->DeleteObject();
	delete m_LegendFont;
	m_LegendFont = new CFont();
	m_LegendFont->CreateFont( int(m_Size.cy*0.08), int(m_Size.cx*0.035), 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
		DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Times New Roman" );

	// Set new graph value font size
	m_ValueFont->DeleteObject();
	delete m_ValueFont;
	m_ValueFont = new CFont();
	m_ValueFont->CreateFont( int(m_Size.cy*0.1), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
		DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial" );
}

void C2DLineGraph::SetFullSize(CSize full_size)
{
	// Set full size
	m_FullSize = full_size;
}

void C2DLineGraph::AddSegment(CString s_text)
{
	// Increment segments number
	m_SegmentsNumber++;

	// Create new graph segment
	_2DLineGraphSegments* newSegment = new _2DLineGraphSegments;
	newSegment->index = m_SegmentsNumber;
	newSegment->text = s_text;
	newSegment->next = NULL;

	// Add new segment to the graph
	_2DLineGraphSegments* curr_seg = m_Segments;
	if ( curr_seg == NULL )
		m_Segments = newSegment;
	else
	{
		while ( curr_seg->next != NULL )
			curr_seg = curr_seg->next;
		curr_seg->next = newSegment;
	}

	// Check graph series number
	if ( m_SeriesNumber > 0 )
	{
		// Create new graph value
		_2DLineGraphValues* newValue = new _2DLineGraphValues;
		newValue->index = m_SegmentsNumber;
		newValue->value = 0;
		newValue->next = NULL;

		// Add new value to the graph segments
		_2DLineGraphSeries* curr_ser = m_Series;
		_2DLineGraphValues* curr_val = NULL;
		while ( curr_ser != NULL )
		{
			// Add new graph value to the graph
			curr_val = curr_ser->values;
			if ( curr_val == NULL )
				curr_ser->values = newValue;
			else
			{
				while ( curr_val->next != NULL )
					curr_val = curr_val->next;
				curr_val->next = newValue;
			}

			// Get next series
			curr_ser = curr_ser->next;
		}
	}
}

void C2DLineGraph::DeleteSegment(int s_index)
{
	// Check index bounds
	if ( ( s_index < 1 ) || ( s_index > m_SegmentsNumber ) )
		AfxMessageBox( "Index is out of bounds...", MB_OK, NULL );
	else
	{
		// Decrement segments number
		m_SegmentsNumber--;

		// Delete segment from the graph
		_2DLineGraphSegments* curr_seg = m_Segments;
		_2DLineGraphSegments* prev_seg = NULL;
		if ( curr_seg != NULL )
		{
			// Find graph segment
			while ( curr_seg->index != s_index )
			{
				prev_seg = curr_seg;
				curr_seg = curr_seg->next;
			}

			// Delete first graph segment
			if ( curr_seg == m_Segments )
				m_Segments = m_Segments->next;
			// Delete other graph segment
			else
				prev_seg->next = curr_seg->next;

			// Delete graph segment
			delete curr_seg;

			// Delete value from the graph series
			_2DLineGraphSeries* curr_ser = m_Series;
			_2DLineGraphValues* curr_val = NULL;
			_2DLineGraphValues* prev_val = NULL;
			while ( curr_ser != NULL )
			{
				// Delete graph value from the graph
				curr_val = curr_ser->values;
				prev_val = NULL;
				if ( curr_val == NULL )
				{
					// Find graph series value
					while ( curr_val->index != s_index )
					{
						prev_val = curr_val;
						curr_val = curr_val->next;
					}

					// Delete first graph series value
					if ( curr_val == curr_ser->values )
						curr_ser->values = curr_ser->values->next;
					// Delete other graph series value
					else
						prev_val->next = curr_val->next;

					// Delete graph value
					delete curr_val;
				}

				// Get net series
				curr_ser = curr_ser->next;
			}
		}
	}
}

void C2DLineGraph::AddSeries(CString s_text, COLORREF s_color)
{
	// Check segments number
	if ( m_SegmentsNumber > 0 )
	{
		// Increment series number
		m_SeriesNumber++;

		// Create new graph series
		_2DLineGraphSeries* newSeries = new _2DLineGraphSeries;
		newSeries->index = m_SeriesNumber;
		newSeries->text = s_text;
		newSeries->color = s_color;
		newSeries->values = NULL;
		newSeries->next = NULL;

		// Add values to new graph series
		_2DLineGraphValues* curr_val = newSeries->values;
		_2DLineGraphValues* newValue = NULL;
		for ( int index=1; index<=m_SegmentsNumber; index++ )
		{
			// Create new graph series value
			newValue = new _2DLineGraphValues;
			newValue->index = index;
			newValue->value = 0;
			newValue->next = NULL;

			// Add new graph value to the graph
			if ( curr_val == NULL )
				newSeries->values = newValue;
			else
				curr_val->next = newValue;
			curr_val = newValue;
		}

		// Add new graph series to the graph
		_2DLineGraphSeries* curr_ser = m_Series;
		if ( curr_ser == NULL )
			m_Series = newSeries;
		else
		{
			while ( curr_ser->next != NULL )
				curr_ser = curr_ser->next;
			curr_ser->next = newSeries;
		}
	}
	else
		AfxMessageBox( "No graph segments...", MB_OK, NULL );
}

void C2DLineGraph::DeleteSeries(int s_index)
{
	// Check index bounds
	if ( ( s_index < 1 ) || ( s_index > m_SeriesNumber ) )
		AfxMessageBox( "Series index is out of bounds...", MB_OK, NULL );
	else
	{
		// Decrement series number
		m_SeriesNumber--;

		// Find graph series
		_2DLineGraphSeries* curr_ser = m_Series;
		_2DLineGraphSeries* prev_ser = NULL;
		while ( curr_ser->index != s_index )
		{
			prev_ser = curr_ser;
			curr_ser = curr_ser->next;
		}

		// Delete graph series values
		_2DLineGraphValues* curr_val = curr_ser->values;
		_2DLineGraphValues* prev_val = NULL;
		while ( curr_val != NULL )
		{
			prev_val = curr_val;
			curr_val = curr_val->next;
			delete prev_val;
		}

		// Delete graph series
		if ( curr_ser == m_Series )
			m_Series = m_Series->next;
		else
			prev_ser->next = curr_ser->next;
		delete curr_ser;
	}
}

void C2DLineGraph::SetValue(int s_segment, int s_series, int s_value)
{
	// Check segment bounds
	if ( ( s_segment < 1 ) || ( s_segment > m_SegmentsNumber ) )
		AfxMessageBox( "Segments index is out of bounds...", MB_OK, NULL );
	else if ( ( s_series < 1 ) || ( s_series > m_SeriesNumber ) )
		AfxMessageBox( "Series index is out of bounds...", MB_OK, NULL );
	else
	{
		// Find graph series
		_2DLineGraphSeries* curr_ser = m_Series;
		while ( curr_ser->index != s_series )
			curr_ser = curr_ser->next;

		// Find graph series value
		_2DLineGraphValues* curr_val = curr_ser->values;
		while ( curr_val->index != s_segment )
			curr_val = curr_val->next;

		// Set graph series value
		curr_val->value = s_value;
	}
}

void C2DLineGraph::SetGraphAnimation(BOOL g_animation, int a_type)
{
	// Set graph animation parameters
	m_Animation = g_animation;
	m_AnimationType = a_type;
	m_AnimationPercent = 0;
	m_AnimationPause = 0;
	m_AnimationPauseStart = FALSE;

	// Check graph animation flag
	if ( m_Animation == TRUE )
	{
		// If setting draw-all graph animation type
		if ( m_AnimationType == AT_LINE_DRAW_ALL )
		{
			// Set animation segments completed
			m_DrawSegmentsCompleted = 0;

			// Set segments percent
			m_SegmentsPercent = int(100.0/double(m_SegmentsNumber));

			// Set graph step parameters
			int seg_area = int(m_Size.cx*0.7);
			int seg_step = int(double(seg_area)/(4*double(m_SegmentsNumber)));
			int ser_step = int(double(seg_step)/8);
			// Calculate graph x and y coordinate system begining
			int startX = m_Position.x + int( m_Size.cx*0.15 );
			int startY = m_Position.y + int( m_Size.cy*0.95 );
			// Set draw segments points
			if ( m_DrawSeriesPoints != NULL )
				delete []m_DrawSeriesPoints;
			m_DrawSeriesPoints = new CPoint[m_SeriesNumber];
			for ( int i=0; i<m_SeriesNumber; i++ )
			{
				if ( m_SegmentsNumber > 1 )
					m_DrawSeriesPoints[i] = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
				else
					m_DrawSeriesPoints[i] = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
			}
		}
		// If setting draw-series graph animation type
		else if ( m_AnimationType == AT_LINE_DRAW_SERIES )
		{
			// Set animation segments completed
			m_DrawSeriesCompleted = 0;

			// Set animation segments completed
			m_DrawSegmentsCompleted = 0;

			// Set segments percent
			m_SegmentsPercent = int(100.0/double(m_SegmentsNumber));

			// Set graph step parameters
			int seg_area = int(m_Size.cx*0.7);
			int seg_step = int(double(seg_area)/(4*double(m_SegmentsNumber)));
			int ser_step = int(double(seg_step)/8);
			// Calculate graph x and y coordinate system begining
			int startX = m_Position.x + int( m_Size.cx*0.15 );
			int startY = m_Position.y + int( m_Size.cy*0.95 );
			// Set draw segments points
			if ( m_DrawSeriesPoints != NULL )
				delete []m_DrawSeriesPoints;
			m_DrawSeriesPoints = new CPoint[m_SeriesNumber];
			for ( int i=0; i<m_SeriesNumber; i++ )
			{
				if ( m_SegmentsNumber > 1 )
					m_DrawSeriesPoints[i] = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
				else
					m_DrawSeriesPoints[i] = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
			}
		}
	}
}

void C2DLineGraph::UpdateAnimation()
{
	if ( m_Animation == TRUE )
	{
		// If running draw-all animation
		if ( m_AnimationType == AT_LINE_DRAW_ALL )
		{
			// Check segments completed
			if ( m_AnimationPercent >= ((m_DrawSegmentsCompleted+1)*m_SegmentsPercent) )
			{
				// Increment segments completed
				m_DrawSegmentsCompleted++;
			}

			// Check animation percent
			if ( m_AnimationPercent >= (m_SegmentsNumber-1)*m_SegmentsPercent )
			{
				if ( m_AnimationPause > 20 )
				{
					// Reset animation percent
					m_AnimationPercent = 0;

					// Reset segments completed
					m_DrawSegmentsCompleted = 0;

					// Reset animation pause
					m_AnimationPause = 0;
					m_AnimationPauseStart = FALSE;

					// Set graph step parameters
					int seg_area = int(m_Size.cx*0.7);
					int seg_step = int(double(seg_area)/(4*double(m_SegmentsNumber)));
					int ser_step = int(double(seg_step)/8);
					// Calculate graph x and y coordinate system begining
					int startX = m_Position.x + int( m_Size.cx*0.15 );
					int startY = m_Position.y + int( m_Size.cy*0.95 );
					// Set draw segments points
					for ( int i=0; i<m_SeriesNumber; i++ )
					{
						if ( m_SegmentsNumber > 1 )
							m_DrawSeriesPoints[i] = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
						else
							m_DrawSeriesPoints[i] = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
					}
				}
				else
				{
					//  Set animation pause flag
					m_AnimationPauseStart = TRUE;

					// Increment animation pause
					m_AnimationPause++;
				}
			}

			if ( m_AnimationPauseStart == FALSE )
			{
				// Increment animation percent
				m_AnimationPercent++;
			}
		}
		// If running draw-series animation
		else if ( m_AnimationType == AT_LINE_DRAW_SERIES )
		{
			// Check segments completed
			if ( m_AnimationPercent >= ((m_DrawSegmentsCompleted+1)*m_SegmentsPercent) )
			{
				// Increment segments completed
				m_DrawSegmentsCompleted++;
			}

			// Check animation percent
			if ( m_AnimationPercent >= (m_SegmentsNumber-1)*m_SegmentsPercent )
			{
				// Reset segments completed
				m_DrawSegmentsCompleted = 0;

				// Increment series completed
				m_DrawSeriesCompleted++;
				
				// Check series animated
				if ( m_DrawSeriesCompleted >= m_SeriesNumber )
				{
					if ( m_AnimationPause > 20 )
					{
						// Reset animation percent
						m_AnimationPercent = 0;

						// Reset series completed
						m_DrawSeriesCompleted = 0;

						// Reset animation pause
						m_AnimationPause = 0;
						m_AnimationPauseStart = FALSE;

						// Set graph step parameters
						int seg_area = int(m_Size.cx*0.7);
						int seg_step = int(double(seg_area)/(4*double(m_SegmentsNumber)));
						int ser_step = int(double(seg_step)/8);
						// Calculate graph x and y coordinate system begining
						int startX = m_Position.x + int( m_Size.cx*0.15 );
						int startY = m_Position.y + int( m_Size.cy*0.95 );
						// Set draw segments points
						for ( int i=0; i<m_SeriesNumber; i++ )
						{
							if ( m_SegmentsNumber > 1 )
								m_DrawSeriesPoints[i] = CPoint( startX + ser_step*4, startY - int(m_Size.cy*0.01) );
							else
								m_DrawSeriesPoints[i] = CPoint( startX + ser_step*2, startY - int(m_Size.cy*0.01) );
						}
					}
					else
					{
						//  Set animation pause flag
						m_AnimationPauseStart = TRUE;

						// Increment animation pause
						m_AnimationPause++;
					}
				}
				else
				{
					// Reset animation percent
					m_AnimationPercent = 0;
				}
			}

			if ( m_AnimationPauseStart == FALSE )
			{
				// Increment animation percent
				m_AnimationPercent += 2;
			}
		}
	}
}

void C2DLineGraph::SetLabelsColor(COLORREF l_color)
{
	// Set graph labels color
	m_LabelsColor = l_color;
}

COLORREF C2DLineGraph::GetLabelsColor()
{
	return m_LabelsColor;
}

void C2DLineGraph::SetLegendBackgroundColor(COLORREF l_bgcolor)
{
	// Set legend background color
	m_LegendBackgroundColor = l_bgcolor;
}

COLORREF C2DLineGraph::GetLegendBackgroundColor()
{
	return m_LegendBackgroundColor;
}

void C2DLineGraph::SetLegendTextColor(COLORREF t_color)
{
	// Set legend text color
	m_LegendTextColor = t_color;
}

COLORREF C2DLineGraph::GetLegendTextColor()
{
	return m_LegendTextColor;
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Elektromehanika d.o.o. Nis
Serbia Serbia
He has a master degree in Computer Science at Faculty of Electronics in Nis (Serbia), and works as a C++/C# application developer for Windows platforms since 2001. He likes traveling, reading and meeting new people and cultures.

Comments and Discussions