Page 1 of 1

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

#1 snoopy11  Icon User is offline

  • Engineering ● Software
  • member icon

Reputation: 770
  • View blog
  • Posts: 2,249
  • Joined: 20-March 10

Posted 09 January 2013 - 07:23 AM

C++ Windows Charting Library Part 4

In this Part 4 I will be discussing how to make a 3D Pie Chart but before I start I would like to go over the normal 2D Pie Chart again as I wasn't that happy with the filling routine for the Pie.

I have redesigned the filling routine to a more logical approach and I am now happier with how the pie in 2D and 3D is being filled.

I will post the entire code again and go over the changes to chartObject::DrawPie then I will discuss the new class chart3D and the new drawing routine chart3D::DrawPie3D

ChartKM.h

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

#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 {

	
public:
	
	void DrawPie3D(HDC hdcDst,int x,int y, int szPieX, int szPieY, COLORREF bkcolor);
};

#endif




ChartKM.cpp

#include <Windows.h>
#include <cmath>
#include <iomanip>
#include <sstream>
#ifdef MSVC
#pragma comment(lib, "libGdi32.a")// link to gdi functions
#endif
#define round(a)(int(a+0.5))
//we need this for windows stuff.

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


}





The section of code that has changed in DrawPie

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



As you can see I have replaced all the complicated if statements with a more logical and concise approach what I am basically doing here is taking the angle of the segment and halving it at the same time I reduce the radius of the circle also by one half. This means I am now filling from the middle of the segment instead of the edge of the segment like I was doing before.

Now we can discuss the chart3d class.

In ChartKM.h

class chart3D:public chartObject {

	
public:
	
	void DrawPie3D(HDC hdcDst,int x,int y, int szPieX, int szPieY, COLORREF bkcolor);
};



This class inherits from the chartObject class and at the moment has only one function.

We will now go on and discuss this function.


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


}




We create a new pen and font and select them into our dc.

We draw a circle and draw our first line.

We set the background color for text then get our Totals same as with ::DrawPie.

We then draw an arc that will be the rink of of our 3D Pie.

I then set up four radii variables

two are for the segments while the other two are for the rink.

xr, yr and Degrees2 are for drawing the rink parts we set those later on.

We set up a loop to loop through all the segments.

I then set up a new COLORREF variable clr to hold a darker shade of the color given.

We do this by working out what is the biggest part of the color Red, Green or Blue and taking 40 off this value to give a slightly darker shade of the original color.

We create two new brushes one for painting the pie and one for the rink.

I then draw the segment lines and then the rink segment lines.

We then paint the segments then the rink.

We draw the Legend and tidy up.

The following is an example program which uses the new class.

main.cpp

#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 */
		600,                 /* The programs width */
		400,                 /* 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 Chart2;
	
	static HDC hdc,memhdc;
	static HBITMAP hbmNewBitMap;
	PAINTSTRUCT ps;

	switch (message)                  /* handle the messages */
	{
	case WM_CREATE:
		{
			

			Chart2.AddData("Cars",140.0f, RGB(255,0,0));
			Chart2.AddData("Vans",50.0f,RGB(0,0,255));
			Chart2.AddData("MotorBikes",60.0f,RGB(0,255,0));
			Chart2.AddData("Bicycles",25.0f,RGB(0,0,128));
			Chart2.AddData("Buses",8.0f,RGB(255,242,0));
			Chart2.AddData("Trucks",25.0f,RGB(255,128,0));
			Chart2.AddTitle("Traffic Survey High Street, Kirby 2003");

			
			



			hdc = GetDC(hwnd);
			memhdc = CreateCompatibleDC(hdc);
			
			//create the bitmap
			hbmNewBitMap=CreateCompatibleBitmap(hdc, 600, 600);
			
			//shove the image into the dc
			SelectObject(memhdc, hbmNewBitMap);
			
			RECT rect;
			rect.left = 0;
			rect.top = 0;
			rect.right = 600;
			rect.bottom = 600;
			//Fill the bitmap with the screen color
			FillRect(memhdc,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
			
			// Draw the Chart onto the memory dc	
			
			Chart2.DrawPie3D(memhdc,25,25,250,150,RGB(0,0,0));

			
			return 0;
		}

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

	case WM_PAINT:
		{

			BeginPaint(hwnd,&ps);


			//Draw the 3d Chart from Memory to Screen device context
			BitBlt(ps.hdc,25,50,400,400,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;
}





The majority of it is standard windows stuff so we will start in WM_CREATE:

we set up a new 3d chart, a device context to the screen , hdc and a memory device context memhdc.

We create a new bitmap.

We add in the chart data..... pretty simple this bit.


We shove the bitmap into our compatible memory device context

We fill the bitmap with our screen color this also fills the memhdc with the screen color too.

We draw the chart onto our memhdc.

In WM_PAINT:

We finally bit blit the memory device context onto our screen device context

Hey presto we now have a 3D Pie Chart.

Next up will be 3D Bar Charts

See you next time.

Snoopy.

This post has been edited by baavgai: 17 February 2013 - 01:43 PM
Reason for edit:: space cleaning


Is This A Good Question/Topic? 0
  • +

Page 1 of 1