Page 1 of 1

C++ Windows Charting Library Part 6 Rate Topic: -----

#1 snoopy11  Icon User is offline

  • Engineering ● Software
  • member icon

Reputation: 840
  • View blog
  • Posts: 2,472
  • Joined: 20-March 10

Posted 16 August 2013 - 10:52 AM

C++ Windows Charting Library Part 6

Well here we are again, this time for the final instalment of this charting library. In this section we discuss how to build 3D line charts.

First of all I would like to apologise to those who have taken the first 5 parts of this tutorial and have had to wait so long for the sixth and final part.
I have been however incredibly busy with work and my two daughters during the summer period but I would have still loved to have finished the library before August that is for sure.

Ok, I will post the entire source code for the library and then will explain the new features not covered in part 5.

ChartKM.h
//ChartKM.h
#ifndef CHARTKM_H_INCLUDED
#define CHARTKM_H_INCLUDED
#include <Windows.h>
#include <vector>
#include <cstdlib>
#include <string>



#ifndef MSVC
#define CLEARTYPE_QUALITY       5
#define CLEARTYPE_NATURAL_QUALITY       6
#endif


using namespace std;


class chartObject
{
protected:

    int Total;
	float Units;
	float Degrees;
	vector<float> Percentage;
	float a;
	float x2;
	float y2;
	float ValueItem;
	unsigned FreqBar;
	float LargestBar;
	int szNames;
	int szInfo;
	int szGroup;
	string xName;
	string yName;
	BOOL vLegend;
	string Title;
	vector<string> Names;
	vector <float> Info;
	vector <float> Frequency;
	vector <float> b;
	vector <COLORREF> colors;
	vector <string> GroupName;
	vector <string> ItemName;
	vector <float>  ItemValue;
	vector <float>  LineValueX;
	vector <float>  LineValueY;
	float  LineRangeX;
	float  LineRangeY;
	vector <int> ItemGroup;

public:

	//constructor
	chartObject();

	//destructor
	~chartObject();




	//Draws Pie Chart
	void DrawPie(HDC hdcDst,int x,int y, int szPieX, int szPieY, COLORREF bkcolor);
	//Draws Bar Chart
	void DrawBar(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor);
	//Draws Line Chart
	void DrawLine(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor);
	//Adds Data for Pie Chart
	void AddData(string Labels, float Data, COLORREF color);
	//Adds Group Data for Bar and Line Charts
	void AddGroupData(string nmGroup, COLORREF color);
	// Adds Item Data for Bar Charts
	void AddItemData(string nmItem, float value, int GroupNo);
	// Adds Item Data for Line Charts
	void AddLineData(string nmItem, float valueX, float valueY);
	// Adds Group and Range Data for Line Charts
	void AddRangeData(float RangeX, float RangeY, int GroupNo);
	// Gets the Totals for Pie Charts
	int GetTotal();
	// Gets the frequency for Pie Charts
	void GetFrequency();
	// Adds a Title to Chart;
	void AddTitle(string szTitle);
	// Adds a Legend to a Pie Chart
	void Legend(BOOL szLegend);
	// Add Titles for x and y axis
	void SetAxisTitles(string szAxisX, string szAxisY);
	// Get Total for Bars
	void GetTotalBar();
    // Get Amount of Groups for Bars
	void GetGroupBar();
	// Get Largest Amount Of Item Data for Bars
	void GetLargestBar();

};



class chart3D:public chartObject {

private:
	HDC MergehDC(HDC hdcSrc, HDC hdcDst,int szX, int szY);
public:

	void DrawPie3D(HDC hdcDst,int x,int y, int szPieX, int szPieY, COLORREF bkcolor);
	void DrawBar3D(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor);
	void DrawLine3D(HDC hdcDst,int x, int y, int szBarX, int szBarY,int bmWidth, int bmHeight, COLORREF bkcolor);
};




#endif




In ChartKM.h, we have two new additions to the Chart3D class. DrawLine3D which deals with drawing 3D line charts.
And a private function which merges two HDCs into one called MergehDC.
This function is only accessible from within the class.
Next up is ChartKM.cpp but before we do that.
We need to go over some of the difficulties in building up a 3D line chart.
First of all if we just try and draw all the lines on top of each other we will get a problem in that we wont be able to fill the line sections in any rational way as there will be lines all over the place, what we want to do is layer each of the drawn lines on top of a single HDC which will carry the image.
So we end up with a decent result like this

Posted Image

ChartKM.cpp

#include <Windows.h>
#include <cmath>
#include <iomanip>
#include <sstream>
#ifdef MSVC
#pragma comment(lib, "gdi32.lib")// link to gdi functions

#endif
#define round(a)(int(a+0.5))

#include "ChartKM.h"






chartObject::chartObject()
{

	szNames =0;
	szInfo = 0;
	Total =0;
	Degrees = 0;
	Title = "";
	a = 0;
	x2 = 0;
	y2 = 0;
	szNames = 0;
	szInfo = 0;
	vLegend = TRUE;
	ValueItem = 0;
	Units = 0.0f;
	ItemValue.clear();
	ItemName.clear();
	GroupName.clear();
}

chartObject::~chartObject()
{

}



void chartObject::DrawPie(HDC hdcDst,int x, int y,int szPieX, int szPieY,COLORREF bkcolor)
{

	float xr = 0.0f;
	float yr = 0.0f;
	float Degrees2 =0.0f;


	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	HFONT font;
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	SelectObject(hdcDst,pen);
	//Draw Circle
	Arc(hdcDst, x,y,szPieX+x,szPieY+y,x,y,x,y);
	//Draw First Line
	MoveToEx(hdcDst,(szPieX/2)+x,(szPieY/2)+y,NULL);
	LineTo(hdcDst,szPieX+x,(szPieY/2)+y);
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	//Gets Totals for Pie Chart
	Total = GetTotal();
	GetFrequency();

	//Draw Lines of arc


	float radius = szPieX/2.0f;
	float yradius = szPieY/2.0f;
	//float y1= 0.0f;
	//Draw segments
	for(int j = 0; j<szInfo; j++)
	{

		HBRUSH hbr = CreateSolidBrush(colors[j]);
		SelectObject(hdcDst,hbr);

		MoveToEx(hdcDst,(szPieX/2)+x,(szPieY/2)+y,NULL);
		Percentage.push_back( Frequency[j]*100 );

		a = Frequency[j]*360;
		if(j==0)
			b.push_back(a);
		else
			b.push_back(b[j-1]+a);
		Degrees= (float)(3.14*b[j])/180;
		x2=(((szPieX/2)+x)+radius*cos(Degrees));
		y2=(((szPieY/2)+y)-yradius*sin(Degrees));
		LineTo(hdcDst,round(x2),round(y2));

		//Fill Segments
		//Fill Pie
		if(j==0)
		{
			Degrees2 = (float)(3.14*(a/2))/180;

		}
		else
		{
			Degrees2= (float)(3.14*(b[j-1]+(a/2)))/180;
		}

		xr = (((szPieX/2)+x)+(radius/2)*cos(Degrees2));
		yr = (((szPieY/2)+y)-(yradius/2)*sin(Degrees2));

		SelectObject(hdcDst,hbr);
		FloodFill(hdcDst,(int)round(xr),(int)round(yr),bkcolor);
	}


	if(Title !="")
	{
		TextOut(hdcDst,(x+szPieX/2)-(Title.length()*2)-Title.size(),y-24,Title.c_str(),Title.size());
	}

	if(vLegend)
	{
		//Draw Legend
		TextOut(hdcDst,x+szPieX+15,y,"Legend",6);
		string buffer;
		stringstream sbuf;
		char buf[20];
		float d = 0.0f;
		for(int i = 0; i<szNames; i++)
		{
			buffer = "";
			sbuf.clear();

			d = Info[i];
			// write strings
			buffer.append(Names[i]);
			buffer.append(" ");
			sbuf << d;
			sbuf >> buf;
			buffer.append(buf);
			TextOut(hdcDst,x+szPieX+40,y+(25*(i+1)),buffer.c_str(),buffer.size());
			//Draw Colored Boxes
			Rectangle(hdcDst,x+szPieX+15,y+(25*(i+1)),x+szPieX+28,y+(25*(i+1)+12));
			RECT rc;
			rc.left= x+szPieX+16;
			rc.top = y+(25*(i+1))+1;
			rc.right = x+szPieX+28-1;
			rc.bottom = y+(25*(i+1)+12)-1;
			HBRUSH hbr = CreateSolidBrush(colors[i]);

			FillRect(hdcDst,&rc,hbr);
		}
	}


	DeleteObject(pen);
	DeleteObject(font);

}

void chartObject::AddData(string Labels, float Data, COLORREF color)
{

	Names.push_back(Labels);
	szNames = Names.size();
	Info.push_back(Data);
	szInfo = Info.size();
	colors.push_back(color);

}

int chartObject::GetTotal()
{
	float Totals=0.0f;

	for(int i = 0; i<szInfo; i++)
	{

		Totals =Totals + Info[i];
	}

	return (int) (Totals+0.5);
}

void chartObject::GetFrequency()
{
	float result;

	for(int i = 0; i<szInfo; i++)
	{
		result = Info[i]/Total;
		Frequency.push_back(result) ;
	}

	return;
}

void chartObject::AddTitle(string szTitle)
{
	Title.assign(szTitle);
}

void chartObject::Legend(BOOL szLegend)
{
	vLegend = szLegend;
}

void chartObject::AddGroupData(string nmGroup, COLORREF color)
{
	GroupName.push_back(nmGroup);
	colors.push_back(color);
}

void chartObject::AddItemData(string nmItem, float value, int GroupNo)
{
	ItemName.push_back(nmItem);
	ItemValue.push_back(value);
	ItemGroup.push_back(GroupNo);
}
void chartObject::SetAxisTitles(string szAxisX, string szAxisY)
{
	xName.assign(szAxisX);
	yName.assign(szAxisY);

}

void chartObject::GetTotalBar()
{
	for(unsigned i = 0; i<ItemValue.size(); i++)
	{
		ValueItem = ValueItem+ItemValue[i];
	}

	ValueItem = ValueItem / (ItemValue.size()+1);
}

void chartObject::GetGroupBar()
{
	FreqBar = GroupName.size();

}

void chartObject::GetLargestBar()
{
	LargestBar  = ItemValue[0];
	for(unsigned i =1; i<ItemValue.size(); i++)
	{
		if(LargestBar<ItemValue[i])
		{
			LargestBar = ItemValue[i];
		}
	}

}


void chartObject::DrawBar(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor)
{
	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	SelectObject(hdcDst,pen);
	HFONT font;
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	font = CreateFont(14,0,900,900,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	//Draw y axis
	MoveToEx(hdcDst,x+25+14,(szBarY)+y,NULL);
	LineTo(hdcDst,x+25+14,y);
	//Draw x axis
	MoveToEx(hdcDst,x+25+14,(szBarY)+y,NULL);
	LineTo(hdcDst,szBarX+x+25+14,(szBarY)+y);

	TextOut(hdcDst,x,((szBarY+y/2)+(yName.length()/2)),yName.c_str(),yName.length());// Draw y-axis label
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	TextOut(hdcDst,((szBarX-x)/2)-(xName.length()/2),y+szBarY+42,xName.c_str(),xName.length());//Draw x-axis label
	TextOut(hdcDst,x+(25),y+szBarY,"0",1);// Draw a zero at origin

	TextOut(hdcDst,((szBarX-x)/2)-(Title.length()/2),y-24,Title.c_str(),Title.length());//Draw Title

	//Get Totals for Bar Chart
	GetTotalBar();
	GetGroupBar();
	GetLargestBar();

	//Draw Graph
	Units = LargestBar/3.0f;
	int graph = szBarY/3;
	for(int i = 0; i<3; i++)
	{
		MoveToEx(hdcDst,x+25+14,(graph*i)+y,NULL);
		LineTo(hdcDst,x+25+14+szBarX+5,(graph*i)+y);
		stringstream  number;
		number << setprecision(3);
		number << (LargestBar-(Units*i));
		string num = "";
		number >> num;
		TextOut(hdcDst,x+25+14+szBarX+7,(graph*i)+y,num.c_str(),num.length());//draw numbers

	}
	for(unsigned i= 0; i<GroupName.size(); i++)
	{
		//draw Legend
		Rectangle(hdcDst,(x+50-12-1),y+szBarY+42+(25*(i+1)-1),(x+50-12-1+12),y+szBarY+42+(25*(i+1)+12));
		RECT rc;
		rc.left= (x+50-12);
		rc.top = y+szBarY+42+(25*(i+1));
		rc.right = (x+50)-2;
		rc.bottom = y+szBarY+42+(25*(i+1)+12)-1;
		HBRUSH hbr = CreateSolidBrush(colors[i]);

		FillRect(hdcDst,&rc,hbr);
		TextOut(hdcDst,(x)+(GroupName[i].length()+50),y+szBarY+42+(25*(i+1)),GroupName[i].c_str(),GroupName[i].length());//Drawlabels


	}
	MoveToEx(hdcDst,x+25+14+szBarX,(y+szBarY),NULL);
	LineTo(hdcDst,x+25+14+szBarX,(y+szBarY+15));
	MoveToEx(hdcDst,x+25+14+szBarX,y,NULL);
	LineTo(hdcDst,x+25+14+szBarX,y+szBarY);
	int amt = (ItemValue.size())/(GroupName.size());
	int sections = (szBarX)/amt;
	int counter = 0;
	int count = 0;
	// Draw Bar Data
	for(unsigned i =0; i<(ItemValue.size()); i=i+GroupName.size())
	{
		counter = (i/GroupName.size())* sections;
		MoveToEx(hdcDst,x+25+14+counter,(y+szBarY),NULL);
		LineTo(hdcDst,x+25+14+counter,(y+szBarY+15));
		TextOut(hdcDst,(x+25+14+counter)+(ItemName[i].length()),(y+szBarY+15),ItemName[i].c_str(),ItemName[i].length());



		for(unsigned j = 0; j<(GroupName.size()); j++)
		{
			RECT rect;
			float result = szBarY-(szBarY*(ItemValue[count]/LargestBar))+y;
			rect.bottom = y+szBarY;
			rect.right =  x+25+14+counter+5+((szBarX/20)*(j+1));
			rect.top =  (LONG)(result);
			rect.left = (x+25+14+counter+5+((szBarX/20)*j));

			HBRUSH hbr = CreateSolidBrush(colors[j]);
			FillRect(hdcDst,&rect,hbr);
			count++;

		}
	}
	DeleteObject(pen);
	DeleteObject(font);
}

void chartObject::AddLineData(string nmItem, float valueX, float valueY)
{
	ItemName.push_back(nmItem);
	LineValueX.push_back(valueX);
	LineValueY.push_back(valueY);
}

void chartObject::AddRangeData(float RangeX, float RangeY, int GroupNo)
{
	LineRangeX = RangeX;
	LineRangeY = RangeY;
	ItemGroup.push_back(GroupNo);

}

void chartObject::DrawLine(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor)
{
	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	SelectObject(hdcDst,pen);
	HFONT font;
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	font = CreateFont(14,0,900,900,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	//Draw y axis
	MoveToEx(hdcDst,x+50+14,(szBarY)+y,NULL);
	LineTo(hdcDst,x+50+14,y);
	//Draw x axis
	MoveToEx(hdcDst,x+50+14,(szBarY)+y,NULL);
	LineTo(hdcDst,szBarX+x+50+14,(szBarY)+y);
	TextOut(hdcDst,x,((szBarY+y/2)+(yName.length()/2)),yName.c_str(),yName.length());// Draw y-axis label
	// change font
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	TextOut(hdcDst,((szBarX+x+50)/2)-(xName.length()/2)+50,y+szBarY+42,xName.c_str(),xName.length());//Draw x-axis label
	TextOut(hdcDst,x+(50),y+szBarY,"0",1);// Draw a zero at origin
	//Draw Range at top of graph y axis
	stringstream ss;
	ss << LineRangeY;
	string number;
	ss >> number;
	TextOut(hdcDst,x+50-(number.length()*4),y-4,number.c_str(),number.length());
	//Draw Range at top of graph x axis
	ss.clear();
	ss << LineRangeX;
	ss >> number;
	TextOut(hdcDst,x+szBarX+50+14-(number.length()*4),y+szBarY+4,number.c_str(),number.length());

	// Add Title
	TextOut(hdcDst,((szBarX+x+50+14)/2)-((Title.length()*4)/2),y-24,Title.c_str(),Title.length());//Draw Title
	// Draw Halfway segment points
	MoveToEx(hdcDst, ((szBarX/2)+x+50+14), y+szBarY,NULL);
	LineTo(hdcDst, ((szBarX/2)+x+50+14), y+szBarY+4);
	ss.clear();
	ss << (LineRangeX/2);
	ss >> number;
	TextOut(hdcDst,(szBarX/2)+50+14+x-((number.length()*4)/2),y+szBarY+6,number.c_str(),number.length());
	MoveToEx(hdcDst,x+50+14,y+(szBarY/2),NULL);
	LineTo(hdcDst, (x+50+14)-4,y+(szBarY/2));
	ss.clear();
	ss << (LineRangeY/2);
	ss >> number;
	TextOut(hdcDst,50+x-((number.length()*4)),y+(szBarY/2),number.c_str(),number.length());
	string container;
	HPEN pen2= CreatePen(PS_SOLID,1,colors[1]);
	int x1=x+50+14;
	int y1=y+szBarY;
	//Start Drawing Data inside loops
	for(unsigned k = 0; k < GroupName.size(); k++)
	{
		container.clear();
		container = GroupName[k];
		x1=x+50+14;
		y1=y+szBarY;
		MoveToEx(hdcDst,x1,y1,NULL);
		for(unsigned j=0; j<GroupName.size(); j++)
		{
			if(GroupName[j] == container)
			{
				pen2 = CreatePen(PS_SOLID,1,colors[j]);
				SelectObject(hdcDst, pen2);
			}
		}

		for(unsigned i=0; i<LineValueX.size(); i++)
		{
			if(ItemName[i] == container)
			{
				x1=  (int)(x+50+14+((LineValueX[i]/LineRangeX)*szBarX));
				y1 = (int)(y +szBarY-((LineValueY[i]/LineRangeY)*szBarY));
				LineTo(hdcDst,x1,y1);
			}
			MoveToEx(hdcDst,x1,y1,NULL);
		}//End loop i
	}//End loop k

	HBRUSH hbr = NULL;
	//Draw Legend
	if(vLegend)
	{
		//Draw Legend
		TextOut(hdcDst,x+szBarX+15+50,y,"Legend",6);
		string buffer;


		//float d = 0.0f;
		for(unsigned i = 0; i<GroupName.size(); i++)
		{
			buffer = "";



			// write strings
			buffer.append(GroupName[i]);
			TextOut(hdcDst,x+szBarX+70+15,y+(25*(i+1)),buffer.c_str(),buffer.size());
			//Draw Colored Boxes
			Rectangle(hdcDst,x+szBarX+15+50,y+(25*(i+1)),x+szBarX+28+50,y+(25*(i+1)+12));
			RECT rc;
			rc.left= x+szBarX+16+50;
			rc.top = y+(25*(i+1))+1;
			rc.right = x+szBarX+28-1+50;
			rc.bottom = y+(25*(i+1)+12)-1;
			hbr = CreateSolidBrush(colors[i]);

			FillRect(hdcDst,&rc,hbr);
		}
	}


	SelectObject(hdcDst,pen);
	DeleteObject(pen);
	DeleteObject(pen2);
	DeleteObject(font);
	DeleteObject(hbr);
}



void chart3D::DrawPie3D(HDC hdcDst,int x,int y, int szPieX, int szPieY, COLORREF bkcolor)
{


	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	HFONT font;
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	SelectObject(hdcDst,pen);
	//Draw Circle
	Arc(hdcDst, x,y,szPieX+x,szPieY+y,x,y,x,y);
	//Draw First Line
	MoveToEx(hdcDst,(szPieX/2)+x,(szPieY/2)+y,NULL);
	LineTo(hdcDst,szPieX+x,(szPieY/2)+y);
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	//Gets Totals for Pie Chart
	Total = GetTotal();
	GetFrequency();

	//Draw Lines of arc

	Arc(hdcDst,x,y,x+szPieX,(int)(y+(szPieY+16)),x,y+(szPieY/2),x+szPieX,y+(szPieY/2));


	float radius = szPieX/2.0f;
	float xrad = (szPieX+8.0f)/2.0f;
	float yradius = (szPieY)/2.0f;
	float yrad = (y+szPieY+8.0f)/2.0f;
	float xr = 0.0f;
	float yr = 0.0f;
	float Degrees2 =0.0f;
	//Draw segments
	for(int j = 0; j<szInfo; j++)
	{
		COLORREF clr = colors[j];
		BYTE R = GetRValue(colors[j]);
		BYTE G = GetGValue(colors[j]);
		BYTE B = GetBValue(colors[j]);
		int Flag =0;
		// Get a darker shade of the primary color for the rink
		if(R>=G&&R>=B)/>/>
		{
			R=R-40;
			clr = RGB(R,G,B)/>/>;
			Flag =1;
		}
		if(G>=R&&G>=B&&Flag==0)
		{
			G=G-40;
			clr = RGB(R,G,B)/>/>;
			Flag=1;
		}
		if(B>=G&&B>=R&&Flag==0)
		{
			B=B-40;
			clr = RGB(R,G,B)/>/>;
			Flag=1;
		}
		HBRUSH hbr = CreateSolidBrush(colors[j]);
		HBRUSH hbr2 = CreateSolidBrush(clr);
		SelectObject(hdcDst,hbr);

		MoveToEx(hdcDst,(szPieX/2)+x,(szPieY/2)+y,NULL);
		Percentage.push_back( Frequency[j]*100 );

		a = Frequency[j]*360;
		if(j==0)
			b.push_back(a);
		else
			b.push_back(b[j-1]+a);
		Degrees= (float)(3.14*b[j])/180;
		x2=(((szPieX/2)+x)+radius*cos(Degrees));
		y2=(((szPieY/2)+y)-yradius*sin(Degrees));
		float x3 = (((szPieX/2)+x)+xrad*cos(Degrees));
		float y3 = (((szPieY/2)+y)-yrad*sin(Degrees));
		// draw segment lines
		LineTo(hdcDst,round(x2),round(y2));
		if(b[j]>180&&b[j]<360)
		{
			LineTo(hdcDst,round(x3),round(y3));
		}
		//Fill Pie
		if(j==0)
		{
			Degrees2 = (float)(3.14*(a/2))/180;

		}
		else
		{
			Degrees2= (float)(3.14*(b[j-1]+(a/2)))/180;
		}

		xr = (((szPieX/2)+x)+(radius/2)*cos(Degrees2));
		yr = (((szPieY/2)+y)-(yradius/2)*sin(Degrees2));

		SelectObject(hdcDst,hbr);
		FloodFill(hdcDst,(int)round(xr),(int)round(yr),bkcolor);
		// Fill Rink
		if(b[j]>190&&b[j]<=350)
		{
			Degrees2 = (float)(3.14*((b[j]-4)))/180;

			xr = (((szPieX/2)+x)+(radius)*cos(Degrees2));
			yr = (((szPieY/2)+y)-(yrad-8)*sin(Degrees2));
			SelectObject(hdcDst,hbr2);
			FloodFill(hdcDst,(int)round(xr),(int)round(yr),bkcolor);
		}

		if(b[j]>350&&b[j]<361)
		{
			Degrees2 = (float)(3.14*((b[j-1]+6)))/180;

			xr = (((szPieX/2)+x)+(radius-2)*cos(Degrees2));
			yr = (((szPieY/2)+y)-(yrad)*sin(Degrees2));
			SelectObject(hdcDst,hbr2);
			FloodFill(hdcDst,(int)round(xr),(int)round(yr),bkcolor);

		}


	}


	if(Title !="")
	{
		TextOut(hdcDst,(x+szPieX/2)-(Title.length()*2)-Title.size(),y-24,Title.c_str(),Title.size());
	}

	if(vLegend)
	{
		//Draw Legend
		TextOut(hdcDst,x+szPieX+15,y,"Legend",6);
		string buffer;
		stringstream sbuf;
		char buf[20];
		float d = 0.0f;
		for(int i = 0; i<szNames; i++)
		{
			buffer = "";
			sbuf.clear();

			d = Info[i];
			// write strings
			buffer.append(Names[i]);
			buffer.append(" ");
			sbuf << d;
			sbuf >> buf;
			buffer.append(buf);
			TextOut(hdcDst,x+szPieX+40,y+(25*(i+1)),buffer.c_str(),buffer.size());
			//Draw Colored Boxes
			Rectangle(hdcDst,x+szPieX+15,y+(25*(i+1)),x+szPieX+28,y+(25*(i+1)+12));
			RECT rc;
			rc.left= x+szPieX+16;
			rc.top = y+(25*(i+1))+1;
			rc.right = x+szPieX+28-1;
			rc.bottom = y+(25*(i+1)+12)-1;
			HBRUSH hbr = CreateSolidBrush(colors[i]);

			FillRect(hdcDst,&rc,hbr);
		}
	}


	DeleteObject(pen);
	DeleteObject(font);


}

void chart3D::DrawBar3D(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor)
{
	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	SelectObject(hdcDst,pen);
	HFONT font;
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	font = CreateFont(14,0,900,900,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	//Draw y axis
	MoveToEx(hdcDst,x+25+14,(szBarY)+y,NULL);
	LineTo(hdcDst,x+25+14,y);
	LineTo(hdcDst,x+25+14+10,y-5);
	LineTo(hdcDst,szBarX+x+25+14+10,y-5);
	LineTo(hdcDst,szBarX+x+25+14+10,(szBarY)+y-10);
	MoveToEx(hdcDst,x+25+14+10,y-5,NULL);
	LineTo(hdcDst,x+25+14+10,szBarY+y-10);
	LineTo(hdcDst,x+25+14,szBarY+y);
	//Draw x axis
	MoveToEx(hdcDst,x+25+14,(szBarY)+y,NULL);
	LineTo(hdcDst,szBarX+x+25+14,(szBarY)+y);
	LineTo(hdcDst,szBarX+x+25+14+10,(szBarY)+y-10);

	TextOut(hdcDst,x,((szBarY+y/2)+(yName.length()/2)),yName.c_str(),yName.length());// Draw y-axis label
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	TextOut(hdcDst,((szBarX-x)/2)-(xName.length()/2),y+szBarY+42,xName.c_str(),xName.length());//Draw x-axis label
	TextOut(hdcDst,x+(25+szBarX+14+10),y+szBarY-5,"0",1);// Draw a zero at origin

	TextOut(hdcDst,((szBarX-x)/2)-(Title.length()/2),y-24,Title.c_str(),Title.length());//Draw Title

	//Get Totals for Bar Chart
	GetTotalBar();
	GetGroupBar();
	GetLargestBar();



	for(unsigned i= 0; i<GroupName.size(); i++)
	{
		//draw Legend
		Rectangle(hdcDst,(x+50-12-1),y+szBarY+42+(25*(i+1)-1),(x+50-12-1+12),y+szBarY+42+(25*(i+1)+12));
		RECT rc;
		rc.left= (x+50-12);
		rc.top = y+szBarY+42+(25*(i+1));
		rc.right = (x+50)-2;
		rc.bottom = y+szBarY+42+(25*(i+1)+12)-1;
		HBRUSH hbr = CreateSolidBrush(colors[i]);

		FillRect(hdcDst,&rc,hbr);
		TextOut(hdcDst,(x)+(GroupName[i].length()+50),y+szBarY+42+(25*(i+1)),GroupName[i].c_str(),GroupName[i].length());//Drawlabels


	}

	int amt = (ItemValue.size())/(GroupName.size());
	int sections = (szBarX)/amt;
	int counter = 0;
	int count = 0;
	// Draw Bar Data
	for(unsigned i =0; i<(ItemValue.size()); i=i+GroupName.size())
	{
		counter = (i/GroupName.size())* sections;
		MoveToEx(hdcDst,x+25+14+counter,(y+szBarY),NULL);
		LineTo(hdcDst,x+25+14+counter,(y+szBarY+15));
		TextOut(hdcDst,(x+25+14+counter)+(ItemName[i].length()),(y+szBarY+15),ItemName[i].c_str(),ItemName[i].length());



		for(unsigned j = 0; j<(GroupName.size()); j++)
		{
			RECT rect;
			COLORREF clr = colors[j];
			BYTE R = GetRValue(colors[j]);
			BYTE G = GetGValue(colors[j]);
			BYTE B = GetBValue(colors[j]);
			int Flag =0;
			// Get a darker shade of the primary color for the sides
			if(R>=G&&R>=B)/>/>
			{
				R=R-60;
				clr = RGB(R,G,B)/>/>;
				Flag =1;
			}
			if(G>=R&&G>=B&&Flag==0)
			{
				G=G-60;
				clr = RGB(R,G,B)/>/>;
				Flag=1;
			}
			if(B>=G&&B>=R&&Flag==0)
			{
				B=B-60;
				clr = RGB(R,G,B)/>/>;
				Flag=1;
			}
			HBRUSH hbr2 = CreateSolidBrush(clr);
			float result = szBarY-(szBarY*(ItemValue[count]/LargestBar))+y;
			rect.bottom = y+szBarY;
			rect.right =  x+25+14+counter+5+((szBarX/20)*(j+1));
			rect.top =  (LONG)(result);
			rect.left = (x+25+14+counter+5+((szBarX/20)*j));

			HBRUSH hbr = CreateSolidBrush(colors[j]);
			SelectObject(hdcDst,hbr);
			FillRect(hdcDst,&rect,hbr);

			MoveToEx(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1)),(LONG)(result),NULL);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1))+10,(LONG)(result)-5);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j))+10,(LONG)(result)-5);
			LineTo(hdcDst,(x+25+14+counter+5+((szBarX/20)*j)),(LONG)(result));
			MoveToEx(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1))+10,(LONG)(result)-5,NULL);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1))+10,y+szBarY-5);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1)),y+szBarY);
			MoveToEx(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1)),(LONG)(result),NULL);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j)),(LONG)(result));
			MoveToEx(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1)),(LONG)(result),NULL);
			LineTo(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1)),y+szBarY);
			// Shade Bars
			SelectObject(hdcDst,hbr2);
			FloodFill(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1))-1,(LONG)(result)-1,bkcolor);
			FloodFill(hdcDst,x+25+14+counter+5+((szBarX/20)*(j+1))+2,(LONG)(result)+2,bkcolor);
			FloodFill(hdcDst,x+25+14+counter+5+((szBarX/20)*(j))+9,(LONG)(result)-3,bkcolor);
			count++;

		}
	}

	//Draw Graph
	COLORREF bkgnd = GetPixel(hdcDst,0,0);
	Units = LargestBar/3.0f;
	int graph = szBarY/3;
	for(int i = 0; i<3; i++)
	{
		MoveToEx(hdcDst,x+25+14+10,(graph*i)+y-5,NULL);
		for(int j=(x+25+14+10); j<=x+25+14+szBarX+15; j++)
		{
			if(GetPixel(hdcDst,j,(graph*i)+y-5)==bkgnd)
			{
				SetPixel(hdcDst,j,(graph*i)+y-5,bkcolor);
			}
		}



		stringstream  number;
		number << setprecision(3);
		number << (LargestBar-(Units*i));
		string num = "";
		number >> num;
		TextOut(hdcDst,x+25+14+szBarX+7+10,(graph*i)+y,num.c_str(),num.length());//draw numbers

	}

	// draw bottom line
	for(int j=(x+25+14+10); j<=x+25+14+szBarX+15; j++)
	{
		if(GetPixel(hdcDst,j,(szBarY+y)-10)==bkgnd)
		{
			SetPixel(hdcDst,j,(szBarY+y)-10,bkcolor);
		}
	}

	DeleteObject(pen);
	DeleteObject(font);

}

void chart3D::DrawLine3D(HDC hdcDst,int x, int y, int szBarX, int szBarY,int bmWidth, int bmHeight, COLORREF bkcolor)
{

	HDC hdc = CreateCompatibleDC(hdcDst);


	HBITMAP hbm;
	hbm = CreateCompatibleBitmap(hdcDst,bmWidth,bmHeight);
	SelectObject(hdc, hbm);

	HPEN pen = CreatePen(PS_SOLID,2,bkcolor);
	SelectObject(hdc,pen);
	HFONT font;
	//Set Background Color for Text
	SetBkColor(hdc,GetPixel(hdcDst,0,0));
	COLORREF fillColor = GetPixel(hdcDst,0,0);
	font = CreateFont(14,0,900,900,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdc, font);



	string container;
	szBarY = szBarY+20;
	int x1=x+50+14;
	int y1=y+szBarY;

	//Start Drawing Data inside loops
	for(unsigned k = 0; k < GroupName.size(); k++)
	{
		container.clear();
		container = GroupName[k];

		RECT rect;
			rect.left = 0;
			rect.top = 0;
			rect.right = bmWidth;
			rect.bottom = bmHeight;
			//Fill the bitmap with the screen color
			FillRect(hdc,&rect, CreateSolidBrush(fillColor));

		x1=x+50+14;
		y1=y+szBarY;

		int y2= y1; int x2= x1;

		MoveToEx(hdc,x1,y1,NULL);
		LineTo(hdc,x2,y2-25);
		MoveToEx(hdc,x1,y1,NULL);


		for(unsigned i=0; i<LineValueX.size(); i++)
		{

				if(ItemName[i] == container)
				{
		        x1=  (int)(x+50+((LineValueX[i]/LineRangeX)*szBarX));//
				y1 = (int)(y +szBarY-((LineValueY[i]/LineRangeY)*szBarY));
				LineTo(hdc,x1,y1);
				MoveToEx(hdc,x2,y2-25,NULL);
				x2= x1; y2= y1-25;
				LineTo(hdc,x2,y2);
				LineTo(hdc,x1,y1);

				HBRUSH brush = CreateSolidBrush(colors[k]);
				SelectObject(hdc, brush);

				 FloodFill(hdc,x1-2,y1-5,bkcolor); // color 3d line segments

				DeleteObject(brush);
				x2= x1; y2= y1;

				}
			MoveToEx(hdc,x1,y1,NULL);
		}//End loop i

		hdcDst = MergehDC(hdc,hdcDst,bmWidth, bmHeight);



	}//End loop k

	RECT rect;
			rect.left = 0;
			rect.top = 0;
			rect.right = bmWidth;
			rect.bottom = bmHeight;
			//Fill the bitmap with the screen color
			FillRect(hdc,&rect,CreateSolidBrush(fillColor));



			x1=x+50+14;
	        y1=y+szBarY;
	//Draw y axis
	MoveToEx(hdc, x1,y1+25,NULL);
	LineTo(hdc,x1,y);
	MoveToEx(hdc, x1,y1+25,NULL);
	LineTo(hdc,x1+14,y1+11);
	LineTo(hdc,x1+14,y-7);
	LineTo(hdc,x1,y);
	MoveToEx(hdc,x1+14,y-7,NULL);
	LineTo(hdc,szBarX+x+25+14+30,y-7);
	LineTo(hdc,szBarX+x+25+14+30,y1-15);
	//Draw x axis
	MoveToEx(hdc,x1-25,y1,NULL);
	LineTo(hdc,szBarX+x+25+14+10,y1);
	MoveToEx(hdc,x1-25,y1,NULL);
	LineTo(hdc,x1-10,y1-15);
	LineTo(hdc,szBarX+x+25+14+30,y1-15);
	LineTo(hdc,szBarX+x+25+14+10,y1);

	TextOut(hdc,x1-20,((y1-25-(yName.length()))),yName.c_str(),yName.length());// Draw y-axis label
	// change font
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdc, font);
	TextOut(hdc,((szBarX+x+50)/2)-(xName.length()/2)+50,y+szBarY+42,xName.c_str(),xName.length());//Draw x-axis label
	TextOut(hdc,x+25,y+szBarY,"0",1);// Draw a zero at origin
	//Draw Range at top of graph y axis
	stringstream ss;
	ss << LineRangeY;
	string number;
	ss >> number;
	TextOut(hdc,x+25-((number.length()*4)),y-4,number.c_str(),number.length());
	//Draw Range at top of graph x axis
	ss.clear();
	ss << LineRangeX;
	ss >> number;
	TextOut(hdc,x+szBarX+50+14-(number.length()*4),y+szBarY+4,number.c_str(),number.length());




	// Add Title
	TextOut(hdc,((szBarX+x1+50)/2)-((Title.length()*4)/2),0,Title.c_str(),Title.length()+1);//Draw Title

	ss.clear();
	ss << (LineRangeX/2);
	ss >> number;
	TextOut(hdc,(szBarX/2)+50+14+x-((number.length()*4)/2),y+szBarY+6,number.c_str(),number.length());
	MoveToEx(hdc,x+50+14,y+(szBarY/2),NULL);
	//LineTo(hdcDst, (x+50+14)-4,y+(szBarY/2));
	ss.clear();
	ss << (LineRangeY/2);
	ss >> number;
	TextOut(hdc,x+25-((number.length()*4)),y+(szBarY/2),number.c_str(),number.length());


	hdcDst = MergehDC(hdc,hdcDst,bmWidth,bmHeight);






	HBRUSH hbr = NULL;
	//Draw Legend
	SetBkColor(hdcDst,fillColor);
	if(vLegend)
	{
		//Draw Legend
		TextOut(hdcDst,x+szBarX+15+60,y,"Legend",6);
		string buffer;


		//float d = 0.0f;
		for(unsigned i = 0; i<GroupName.size(); i++)
		{
			buffer = "";



			// write strings
			buffer.append(GroupName[i]);
			TextOut(hdcDst,x+szBarX+70+35,y+(25*(i+1)),buffer.c_str(),buffer.size());
			//Draw Colored Boxes
			Rectangle(hdcDst,x+szBarX+15+60,y+(25*(i+1)),x+szBarX+28+60,y+(25*(i+1)+12));
			RECT rc;
			rc.left= x+szBarX+16+60;
			rc.top = y+(25*(i+1))+1;
			rc.right = x+szBarX+28-1+60;
			rc.bottom = y+(25*(i+1)+12)-1;
			hbr = CreateSolidBrush(colors[i]);

			FillRect(hdcDst,&rc,hbr);
		}
	}



	DeleteObject(pen);
	DeleteObject(font);
	DeleteObject(hbr);
	DeleteObject(hbm);
	DeleteDC(hdc);

}


HDC chart3D::MergehDC(HDC hdcSrc, HDC hdcDst,int szX, int szY)
{


	COLORREF clrTP, clrBK;

	int nRow, nCol;

    clrTP = GetPixel(hdcDst,0,0);// get color at pixel 0,0


	for (nRow = 0; nRow <szY; nRow++)// work our way through all the pixels 
	{
		for (nCol = 0; nCol < szX; nCol++)
		{
			clrBK = GetPixel(hdcSrc,nCol,nRow);
			if (GetPixel(hdcDst,nCol,nRow)==clrTP)// when we hit our set transparency color copy a pixel from one hdc to the other.
			{
				SetPixel(hdcDst, nCol, nRow, clrBK);
			}
		}
	}

	return hdcDst;// return our transformed hdc
}





The Explanation Part.

Ok, now that we have some background on how to achieve this look we will go into it in detail.
void chart3D::DrawLine3D(HDC hdcDst,int x, int y, int szBarX, int szBarY,int bmWidth, int bmHeight, COLORREF bkcolor)
Right this function takes a destination HDC hdcDst, x which is an offset, y which is an offset, szBarX which is the size of the actual chart bit within the bitmap, bmWidth and bmHeight which is the Width and Height of the bitmap being used by hdcDst.
In this function we need the extra bitmap dimensions because we will be creating a temporary bitmap and hdc to merge our 3D lines into one graph or chart.
In the function we create a compatible temporary HDC which is compatible with the destination HDC.
We then create a temporary HBITMAP which is compatible with the destination HDC.
We select this HBITMAP into the temporary HDC. We create a new pen used for drawing the straight lines, the color of which is specified by bkcolor. We then set the background color for drawing Text. We create a new font.

We then set up some variables used in the calculations. Now we loop through GroupNames to identify how many 3D lines we need to draw. We completely fill our new temporary HDC with the background color specified in hdcDst. This is called fillColor and should be selected to match whatever color the window is to get a seamless effect. We set up some x,y variables used in calculating the outline of the 3D line segment and draw our first line which is a horizontal line at the bottom of the 3D line segment.
We then loop through all the line values from this we calculate the x,y cords of our graph or chart for each of the 3D line segments and draw each 3D line segment which consists of 3 lines plus the bottom line which we drew first to create a rectangle which we then fill with whatever fill color we specified in AddGroupData. We create a solid brush with this color and select it into our temporary HDC which is called hdc. We call FloodFill to fill the segment. We then delete our solid brush and let x2 and y2 equal x1 and y1 respectively this lets us draw from where we left off from the last segment so all the segments are joined in a 3D line effect. We end loop I which is responsible for drawing and filling each 3D line and then this is the clever bit where we merge a single 3D line with the destination HDC this produces a layer effect where the first line drawn takes precedence over the second line drawn and so on until all lines are drawn for instance the second line drawn will take precedence over the third line drawn and so on this builds up a realistic 3D line chart where the first line does not get drawn over by preceding lines. The function that does this is called MergehDC and we will discuss how this works later.
We end loop k which contains the Groups and clear our temporary hdc by filling it with the fillColor which is the color of the first pixel in our destination HDC hdcDst. We can now draw our x and y axis now this may seem backwards as in the other functions we usually draw our x and y axis first and draw over the top of that but we are going to use MergehDC again so we want to draw our axis last so it is at the bottom of the line of precedence that is all other objects will be on top of the x and y axis.
We draw all our y and x axis stuff put in labels and titles and draw in the ranges of our graph which is specified in AddRangeData.

We use MergehDC one last time to merge our 3D lines with our newly drawn axis, labels and titles this creates a near complete graph, we then use our destination HDC hdcDst to draw our Legend, a Legend is just a color coded key to identify what the lines represent. We finally delete all our temporary objects and HDCs.
HDC chart3D::MergehDC(HDC hdcSrc, HDC hdcDst,int szX, int szY)

This function is responsible for layering two hdcs together.
It is a slow function due to the use of GetPixel which is in itself a slow function
It could be speeded up by using direct2D functions but I am endeavouring to keep this library as simple as possible so I am sacrificing speed over simplicity.
For what it does its a very simple small function, we setup two COLORREF variables colorTP and colorBK which is a variable to hold the Transparency and Background colors respectively. We then loop through the rows and columns of our bitmap if our destination pixel matches colorTP then set the pixel of the same location from our source HDC to our destination HDC.
Return our destination HDC.
That concludes the explanation Part.

An example using the 3D line chart making capability of ChartKM

#include <Windows.h>
#include <ChartKM.h>

#pragma comment(lib, "ChartKM.lib")



/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpszArgument,
	int nCmdShow)
{
	HWND hwnd;               /* This is the handle for our window */
	MSG messages;            /* Here messages to the application are saved */
	WNDCLASSEX wincl;        /* Data structure for the windowclass */

	/* The Window structure */
	wincl.hInstance = hThisInstance;
	wincl.lpszClassName = szClassName;
	wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
	wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
	wincl.cbSize = sizeof (WNDCLASSEX);

	/* Use default icon and mouse-pointer */
	wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
	wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
	wincl.lpszMenuName = NULL;                 /* No menu */
	wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
	wincl.cbWndExtra = 0;                      /* structure or the window instance */
	/* Use Windows's default colour as the background of the window */
	wincl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

	/* Register the window class, and if it fails quit the program */
	if (!RegisterClassEx (&wincl))
		return 0;

	/* The class is registered, let's create the program*/
	hwnd = CreateWindowEx (
		0,                   /* Extended possibilites for variation */
		szClassName,         /* Classname */
		"Charting Demo Application.",       /* Title Text */
		WS_OVERLAPPEDWINDOW, /* default window */
		CW_USEDEFAULT,       /* Windows decides the position */
		CW_USEDEFAULT,       /* where the window ends up on the screen */
		700,                 /* The programs width */
		500,                 /* and height in pixels */
		HWND_DESKTOP,        /* The window is a child-window to desktop */
		NULL,                /* No menu */
		hThisInstance,       /* Program Instance handler */
		NULL                 /* No Window Creation data */
		);

	/* Make the window visible on the screen */
	ShowWindow (hwnd, nCmdShow);

	/* Run the message loop. It will run until GetMessage() returns 0 */
	while (GetMessage (&messages, NULL, 0, 0))
	{
		/* Translate virtual-key messages into character messages */
		TranslateMessage(&messages);
		/* Send message to WindowProcedure */
		DispatchMessage(&messages);
	}

	/* The program return-value is 0 - The value that PostQuitMessage() gave */
	return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	static chart3D Chart;
	static chart3D Chart2;
	static chart3D Chart3;
	static HDC hdc,memhdc;
	static HBITMAP hbmNewBitMap;
	PAINTSTRUCT ps;

	switch (message)                  /* handle the messages */
	{
	case WM_CREATE:
		{
			Chart3.SetAxisTitles("Time in Hours", "Amount of Vehicles");
			Chart3.AddGroupData("Cars",RGB(255,0,0));
			Chart3.AddGroupData("Vans",RGB(0,0,255));
			Chart3.AddGroupData("Trucks",RGB(163,73,164));
			Chart3.AddRangeData(6.0f,150.0f,3);
			Chart3.AddTitle("Traffic Census, Kirby 2003");

			Chart3.AddLineData("Cars",1.0f,25.0f);
			Chart3.AddLineData("Cars",2.0f,45.0f);
			Chart3.AddLineData("Cars",3.0f,90.0f);
			Chart3.AddLineData("Cars",4.0f,120.0f);
			Chart3.AddLineData("Cars",5.0f,140.0f);
			Chart3.AddLineData("Cars",6.0f,120.0f);
			
			Chart3.AddLineData("Vans",1.0f,20.0f);
			Chart3.AddLineData("Vans",2.0f,40.0f);
			Chart3.AddLineData("Vans",3.0f,80.0f);
			Chart3.AddLineData("Vans",4.0f,90.0f);
			Chart3.AddLineData("Vans",5.0f,110.0f);
			Chart3.AddLineData("Vans",6.0f,140.0f);

			Chart3.AddLineData("Trucks",1.0f,10.0f);
			Chart3.AddLineData("Trucks",2.0f,20.0f);
			Chart3.AddLineData("Trucks",3.0f,60.0f);
			Chart3.AddLineData("Trucks",4.0f,80.0f);
			Chart3.AddLineData("Trucks",5.0f,60.0f);
			Chart3.AddLineData("Trucks",6.0f,80.0f);
			
			hdc = GetDC(hwnd);
			memhdc = CreateCompatibleDC(hdc);
			
			//create the bitmap
			hbmNewBitMap=CreateCompatibleBitmap(hdc, 600, 400);
			
			//shove the image into the dc
			SelectObject(memhdc, hbmNewBitMap);
			
			RECT rect;
			rect.left = 0;
			rect.top = 0;
			rect.right = 600;
			rect.bottom = 400;
			//Fill the bitmap with the screen color
			FillRect(memhdc,&rect,CreateSolidBrush(RGB(255,255,255)));
			

			// Draw the Chart onto the memory dc	
			Chart3.DrawLine3D(memhdc,50,50,200,200,600,400,RGB(0,0,0));
			ReleaseDC(hwnd,memhdc);
			DeleteDC(hdc);
			return 0;
		}

	case WM_SIZE:
		{
            InvalidateRect(hwnd,NULL,TRUE);
			return 0;
		}

	case WM_PAINT:
		{

			BeginPaint(hwnd,&ps);

			
			//Draw the Charts from Memory to Screen device context
			BitBlt(ps.hdc,25,50,600,600,memhdc,0,0,SRCCOPY);

			
			EndPaint(hwnd,&ps);

			return 0;
		}


	case WM_DESTROY:
		PostQuitMessage (0); /* send a WM_QUIT to the message queue */
		DeleteDC(memhdc); 
		
		DeleteObject((HBITMAP)hbmNewBitMap);
		
		break;
	default:                      /* for messages that we don't deal with */
		return DefWindowProc (hwnd, message, wParam, lParam);
	}

	return 0;
}




Most of it is just standard windows stuff so will start at WM_CREATE . We first set the x and y titles add the group data, range data and set the title of the chart.
We then add the individual line data just as we did for the 2D line chart.
We create a blank bitmap and shove it into a memory DC which is compatible with the window. We make a RECT structure which holds the size of our bitmap and fill it with the window color. I then use the DrawLine3D method from ChartKM to draw the chart onto the Memory DC.

Why use HDCs ?


HDCs under windows are important they are handles to device contexts. The device context could be a screen context or a printer context or something else. This means we can print out our charts to the screen or printer or something else. So you can see the obvious uses of having the charts inside a HDC.
In WM_PAINT now we have everything on a HDC from another method (in this case its WM_CREATE) we can simply paint the window hdc by using BitBlt as normal, do not try and set up the chart inside WM_PAINT as it will lead to slow repainting routines on resize and minimize/maximize events.
That is all for now, the Charting Library is intended to be extended, it is to be used as a starting point for those who need a free windows charting library and found the alternatives to be some what lacking.

Obvious things to extend.

Add vector maps, by that I mean a map of North America or Europe that can be resized. This is a fairly easy thing to do and just takes time getting the data in.
Add Geographical projections.
Add 3D Plots, like 3D sine wave plots etc.
Add contouring.
All the above can be easily, well fairly easily be added to the library and while I leave the reader to implement those functions that they individually require. I may from time to time expand the Library by means of additional Tutorials.
But for now I consider the Library to be functional and finished.
Thanks for reading all six of this series. This has been tested under MSVC and MinGW compilers if you are using the MinGW gcc compiler you will have to link to gdi32 library for the ChartKM and obviously ChartKM for the example code. If you are using MSVC the linking is done automatically through the use of #pragma comment(s).



Best Wishes
Snoopy.

Is This A Good Question/Topic? 3
  • +

Page 1 of 1