www.pudn.com > CDrawLine.zip > Graph.cpp


// Graph.cpp : implementation file 
 
 
#include "stdafx.h" 
#include "CDrawLine.h"//应改为对应应用程序的头文件 
#include "GraphSeries.h" 
#include "GraphLegend.h" 
#include "math.h" 
#include "Graph.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
#define GREEN		RGB(0,255,0) 
#define BLUE		RGB(0,0,255) 
#define RED			RGB(255,0,0) 
#define YELLOW		RGB(255,255,0) 
#define ORANGE		RGB(255,153,51) 
#define HOT_PINK	RGB(255,51,153) 
#define PURPLE		RGB(153,0,204) 
#define CYAN		RGB(0,255,255) 
#define BLACK		RGB(0,0,0) 
#define WHITE		RGB(255,255,255) 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CGraph 
 
CGraph::CGraph() 
{ 
	graphSeries = new CObList(); 
	tickRange = 100; 
	seriesSize = 0; 
 
	graphHasLegend = FALSE; 
	legendWidth = 0; 
 
	graphType = 0; 
//以下为设置图例的背景色 
	dataColor[0] = GREEN;  //green 
	dataColor[1] = BLUE;  //blue 
	dataColor[2] = RED;  //red 
	dataColor[3] = YELLOW;  //yellow 
	dataColor[4] = ORANGE;  //orange 
	dataColor[5] = HOT_PINK;  //hot pink 
	dataColor[6] = PURPLE;  //purple 
	dataColor[7] = CYAN;  //cyan 
	dataColor[8] = BLACK;  //black 
	dataColor[9] = WHITE;  //white 
//以上为图例的背景色 
	xAxisAlign = 0;  //horizontal 
	xAxisLabelLength = 0; 
 
	xTickFontSize = 12;//x轴的单元格字体大小 
	yTickFontSize = 12;//y轴的单元格字体 
	legendFontSize = 12;//图例中的字体大小 
	m_DrawMode=DEFAULTMODE; 
} 
 
CGraph::CGraph(int type,int drawmode) 
{ 
	graphSeries = new CObList(); 
	tickRange = 100; 
	seriesSize = 0; 
 
	graphHasLegend = FALSE; 
	legendWidth = 0; 
 
	m_DrawMode=drawmode; 
	graphType = type; 
//以下为设置图例的背景色 
	dataColor[0] = GREEN;  //green 
	dataColor[1] = BLUE;  //blue 
	dataColor[2] = RED;  //red 
	dataColor[3] = YELLOW;  //yellow 
	dataColor[4] = ORANGE;  //orange 
	dataColor[5] = HOT_PINK;  //hot pink 
	dataColor[6] = PURPLE;  //purple 
	dataColor[7] = CYAN;  //cyan 
	dataColor[8] = BLACK;  //black 
	dataColor[9] = WHITE;  //white 
//以上为图例的背景色 
	xAxisAlign = 0;  //horizontal 
	xAxisLabelLength = 0; 
 
	xTickFontSize = 12;//x轴的单元格字体大小 
	yTickFontSize = 12;//y轴的单元格字体大小 
	legendFontSize =16;//图例中的字体大小 
} 
 
CGraph::~CGraph() 
{ 
	POSITION pos; 
	CGraphSeries* pSeries; 
 
	for( pos = graphSeries->GetHeadPosition(); pos != NULL; ) 
	{ 
		pSeries = (CGraphSeries*) graphSeries->GetNext( pos ); 
		delete pSeries; 
	} 
 
	graphSeries->RemoveAll(); 
	delete graphSeries; 
} 
 
 
BEGIN_MESSAGE_MAP(CGraph, CStatic) 
	//{{AFX_MSG_MAP(CGraph) 
		// NOTE - the ClassWizard will add and remove mapping macros here. 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CGraph message handlers 
 
void CGraph::SetTickSpace(int yDist) 
{ 
	tickSpace = yDist; 
} 
 
void CGraph::SetTickRange(int maxTick) 
{ 
	tickRange = maxTick; 
} 
 
void CGraph::SetGraphType(int gType) 
{ 
	graphType = gType; 
} 
 
void CGraph::SetXAxisAlignment(int alignValue) 
{ 
	xAxisAlign = alignValue; 
} 
 
int CGraph::GetXAxisAlignment() 
{ 
	return xAxisAlign; 
} 
 
void CGraph::DrawGraph(CDC* pDC) 
{ 
	CString tickLabel; 
	CWnd* graphWnd = pDC->GetWindow();//此调用得到pDC对应的窗口,即此绘图的区域 
	CRect graphRect; 
	graphWnd->GetClientRect(&graphRect); 
 
	//reset graph to be clear background 
	COLORREF backColor; 
	backColor = RGB(255,255,255);  //设置你所希望的背景色 
	m_background=backColor; 
	CBrush backBrush (backColor); 
	CBrush* pOldBackBrush; 
	pOldBackBrush = pDC->SelectObject(&backBrush); 
	pDC->Rectangle(0, 0, graphRect.Width(), graphRect.Height()); 
	pDC->SelectObject(pOldBackBrush); 
    pDC->SetBkMode(TRANSPARENT); 
	maxHeight = graphRect.Height() - 20;  //for frame window and status bar 
	maxWidth = graphRect.Width() - 5;  //for frame window 
	//We will leave 5 pixels blank on all sides of the graph.  So 
	//top-left side of graph is at 5,5 and the bottom-right side of 
	//graph is at ((maxHeight - 5), (maxWidth - 5)) 
	//these settings are altered by axis labels and legends. 
 
	//draw graph title 
	CFont titleFont;//设置标题的自体 
	titleFont.CreateFont(28, 0, 0, 0, 700, FALSE, FALSE, 0, 
		ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
	CFont* pOldFont = (CFont*) pDC->SelectObject(&titleFont); 
 
	pDC->TextOut((maxWidth / 2) - ((graphTitle.GetLength() * 12) / 2), 
				10, graphTitle);//绘制标题的位置 
	pDC->SelectObject(pOldFont); 
     
	if(graphType == 2)  //pie 
	{ 
 
		//since pie does not have axis lines, set to full size minus 5 on each side 
		//these are needed for legend to plot itself 
		xAxisWidth = maxWidth - 10; 
		yAxisHeight = maxHeight - 20;  //10 buffer and 20 for title 
		xApexPoint = 5; 
		yApexPoint = maxHeight - 5; 
	} 
	else 
	{ 
		//X-axis will be raised by 20 pixels for tick-labels 
		//Y-axis will be shifted by (max(tick-label size) * 10) + label * 10 
		 
		tickLabel.Format("%d", tickRange); 
 
		//determine axis specifications  
			//tick label offset is 4 (for the tick) + 6 (total 10) from axis line 
		xApexPoint = 5 + (tickLabel.GetLength() * (yTickFontSize/ 2 )) + 10 + 30/*(axisYLabel.GetLength() * 8)*/ + 5; //allowing 8 pixels per char in tick label 
			//y apex based on 5 + 15 (x label) + 4 (for the tick) + 4 (text below tick) + 12 (tick label) + 10 
		if(!xAxisAlign)  //horizontal 
			yApexPoint = (maxHeight - 5) - 45;		//apex points are the cross section of axis lines 
		else 
			yApexPoint = (maxHeight - 5) - (xAxisLabelLength * (xTickFontSize / 2)) - 10; 
		yAxisHeight = yApexPoint - 40;//从客户区往下40个像素 
		xAxisWidth = (maxWidth - 5) - xApexPoint;//实际长度为maxwidth-5 
	} 
 
	//draw legend 
	if(graphHasLegend) 
		DrawLegend(pDC); 
 
	if(graphType != 2)  //pie 
	{ 
		//draw axis lines 
		DrawAxis(pDC); 
	} 
 
	//draw series data and labels 
	DrawSeries(pDC); 
 
} 
 
void CGraph::DrawAxis(CDC* pDC) 
{ 
	pDC->SetTextColor(RGB(0,0,0)); 
    BOOL  drawarrow=FALSE; 
	//draw y axis 
	double tickScale = 0.00;//tickScale按比例缩小y轴单元 
	if(tickRange > yAxisHeight) 
		tickScale = ((yAxisHeight * 1.00) / (tickRange * 1.00)) * tickSpace; 
	else tickScale = tickSpace; 
	int ymutiple; 
	ymutiple = tickRange / tickSpace;  
	m_nUnits=yAxisHeight/ymutiple;//设置每个逻辑单元的像素数目 
	int ylength = yApexPoint - (ymutiple* tickScale); 
	 
 
  if(m_DrawMode==DEFAULTMODE) 
  { 
	  pDC->MoveTo(xApexPoint, yApexPoint);   
	 
	 pDC->LineTo(xApexPoint, ylength);//(yApexPoint - yAxisHeight)被ylength替换 
  } 
  else 
 
  { 
      pDC->MoveTo(xApexPoint, yApexPoint);  
      pDC->LineTo(xApexPoint, yApexPoint - yAxisHeight); 
  } 
 
 
	//draw x axis 
  POINT xaxisRight; 
	pDC->MoveTo(xApexPoint, yApexPoint);   
	if(graphHasLegend) 
	{ 
		pDC->LineTo(xApexPoint + (xAxisWidth - legendWidth - 10), yApexPoint); 
	    xaxisRight.x=xApexPoint+ (xAxisWidth - legendWidth - 10); 
		 
	} 
	else 
	{ 
		pDC->LineTo(xApexPoint + xAxisWidth, yApexPoint); 
		xaxisRight.x=xApexPoint + xAxisWidth; 
         
	} 
    xaxisRight.y =yApexPoint; 
	//绘制x轴的箭头 
 
    POINT   pointx4[4]; 
			pointx4[0].x=xaxisRight.x; 
			pointx4[0].y=xaxisRight.y; 
			pointx4[1].x=xaxisRight.x-10; 
            pointx4[1].y=xaxisRight.y+4; 
		    pointx4[2].x=xaxisRight.x-8; 
		    pointx4[2].y=xaxisRight.y; 
            pointx4[3].x=xaxisRight.x-10; 
		    pointx4[3].y=xaxisRight.y-4; 
		    HRGN arrowrgnx; 
			arrowrgnx=::CreatePolygonRgn(pointx4,4,WINDING); 
			 
	       ::Polygon(pDC->m_hDC,pointx4,4); 
	       ::InvertRgn(pDC->m_hDC,arrowrgnx); 
 
 
//以上绘制x轴的箭头 
 
	//draw labels 
	CFont sideFont; 
	sideFont.CreateFont(16, 0, 900, 0, 700, FALSE, FALSE, 0, 
		ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");//创建y轴标签字体 
	CFont* pOldFont = (CFont*) pDC->SelectObject(&sideFont); 
 
	pDC->TextOut(10, (maxHeight / 2) + ((axisYLabel.GetLength() / 2) * 4), axisYLabel); 
	pDC->SelectObject(pOldFont); 
 
	pDC->TextOut(xApexPoint + (xAxisWidth / 2) - ((axisXLabel.GetLength() / 2) * 19), maxHeight - 5 - 6, axisXLabel); 
 
	 
	 
 
	//draw y axis ticks 
	for(int y = 1; y <= tickRange / tickSpace; y++)  //no tick at 0 
	{ 
		int tickYLocation = yApexPoint - (y * tickScale); 
       
		if(y < tickRange / tickSpace&&m_DrawMode==DEFAULTMODE) 
		{ 
		    
		   pDC->MoveTo(xApexPoint - 4, tickYLocation); 
		   pDC->LineTo(xApexPoint + 4, tickYLocation); 
		} 
		else if(m_DrawMode==DEFAULTMODE)//绘制y轴顶端的箭头(其中点为坐标值) 
		{ 
			 
			POINT point4[4]; 
			point4[0].x=xApexPoint; 
			point4[0].y=tickYLocation-6; 
			point4[1].x=xApexPoint-4; 
            point4[1].y=tickYLocation+6; 
		    point4[2].x=xApexPoint; 
		    point4[2].y=tickYLocation+2; 
            point4[3].x=xApexPoint+4; 
		    point4[3].y=tickYLocation+6; 
		    HRGN arrowrgn; 
			arrowrgn=::CreatePolygonRgn(point4,4,WINDING); 
			 
	       ::Polygon(pDC->m_hDC,point4,4); 
	       ::InvertRgn(pDC->m_hDC,arrowrgn); 
			 
		} 
		else 
		{ 
			tickYLocation = yApexPoint - (y *m_nUnits); 
			if(drawarrow==FALSE) 
			{ 
               	POINT point4[4]; 
			    point4[0].x=xApexPoint; 
				point4[0].y=yApexPoint -yAxisHeight-6; 
				point4[1].x=xApexPoint-4; 
				point4[1].y=yApexPoint -yAxisHeight+6; 
				point4[2].x=xApexPoint; 
				point4[2].y=yApexPoint -yAxisHeight+2; 
				point4[3].x=xApexPoint+4; 
				point4[3].y=yApexPoint -yAxisHeight+6; 
				HRGN arrowrgn; 
				arrowrgn=::CreatePolygonRgn(point4,4,WINDING); 
			 
				::Polygon(pDC->m_hDC,point4,4); 
				::InvertRgn(pDC->m_hDC,arrowrgn); 
				drawarrow=TRUE; 
			 
			} 
			    
 
				pDC->MoveTo(xApexPoint - 4, tickYLocation); 
		        pDC->LineTo(xApexPoint + 4, tickYLocation); 
 
		} 
 
		//draw tick label 
		CString tickLabel; 
		tickLabel.Format("%d", y * tickSpace); 
 
		CFont yFont; 
		yFont.CreateFont(yTickFontSize, 0, 0, 0, 700, FALSE, FALSE, 0, 
			ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
		pOldFont = (CFont*) pDC->SelectObject(&yFont); 
 
		pDC->TextOut(xApexPoint - 10 - (tickLabel.GetLength() * (yTickFontSize / 2)), tickYLocation - 6, tickLabel); 
 
		pDC->SelectObject(pOldFont); 
 
	} 
 
	//draw X axis tick marks 
	POSITION pos; 
	pos = graphSeries->GetHeadPosition(); 
	CGraphSeries* tmpSeries; 
	for(int x = 1; x <= graphSeries->GetCount(); x++) 
	{ 
		tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
		int seriesSpace; 
		int tickXLocation; 
		if(graphHasLegend) 
			seriesSpace= (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
		else 
			seriesSpace= xAxisWidth / graphSeries->GetCount(); 
		tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2)); 
 
		pDC->MoveTo(tickXLocation,yApexPoint - 4); 
		pDC->LineTo(tickXLocation,yApexPoint + 4); 
 
		//draw tick label 
		CString tickLabel; 
		tickLabel = tmpSeries->GetLabel(); 
		if(!xAxisAlign)  //horizontal 
			pDC->TextOut(tickXLocation - ((tickLabel.GetLength() * 8) / 2), yApexPoint + 8, tickLabel); 
		else 
		{ 
			CFont sideFont2; 
			sideFont2.CreateFont(xTickFontSize, 0, (xAxisAlign * 10), 0, 700, FALSE, FALSE, 0, 
				ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
				DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
			pOldFont = (CFont*) pDC->SelectObject(&sideFont2); 
			if(xAxisAlign < 180) 
				pDC->TextOut(tickXLocation - 8, yApexPoint + 8 + (xAxisLabelLength * (xTickFontSize / 2)), tickLabel); 
			else 
				pDC->TextOut(tickXLocation + 2, yApexPoint + 8, tickLabel); 
 
			pDC->SelectObject(pOldFont); 
		} 
	} 
 
 
} 
 
void CGraph::AddSeries(CGraphSeries* dataSet) 
{ 
	int numData = 0; 
	for(int i = 0; i < 10; i++) 
	{ 
		if(dataSet->GetData(i) > 0) 
			numData++; 
		if(dataSet->GetLabel().GetLength() > xAxisLabelLength) 
			xAxisLabelLength = dataSet->GetLabel().GetLength(); 
	} 
	if(numData > seriesSize) 
		seriesSize = numData; 
 
	graphSeries->AddTail(dataSet); 
} 
 
void CGraph::SetXAxisLabel(CString label) 
{ 
	axisXLabel = label; 
} 
void CGraph::SetYAxisLabel(CString label) 
{ 
	axisYLabel = label; 
} 
 
void CGraph::SetColor(int group, COLORREF groupColor) 
{ 
	dataColor[group] = groupColor; 
} 
 
COLORREF CGraph::GetColor(int group) 
{ 
	return dataColor[group]; 
} 
 
void CGraph::DrawSeries(CDC* pDC) 
{ 
	int barWidth; 
	int dataPlotSize;   //used to plot rects of data 
	int barL, barT, barR, barB; 
	int tickXLocation; 
	int seriesSpace; 
	double barHeight;  //for scalability 
	POSITION pos; 
	CGraphSeries* tmpSeries; 
 
	if(graphType == 0) //bar 
	{ 
		//determine width of barchart data blocks每个系列占等宽,好象有缺陷 
		if(graphHasLegend) 
			seriesSpace = (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
		else 
			seriesSpace = xAxisWidth / graphSeries->GetCount(); 
		barWidth = (seriesSpace * .6) / seriesSize; 
		dataPlotSize = seriesSize * barWidth;//图块区占.6 
 
		pos = graphSeries->GetHeadPosition(); 
 
		for(int x = 1; x <= graphSeries->GetCount(); x++) 
		{ 
			tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
			tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace/2)); 
 
			for(int s = 0; s < seriesSize; s++) 
			{ 
				barHeight = 0.00; 
				//to allow scalability (height may be less than tickRange) 
				double dataScale = 0.00; 
				if(tickRange > yAxisHeight) 
					dataScale = (yAxisHeight * 1.00) / (tickRange * 1.00); 
				else dataScale = 1; 
				if(tickRange > tmpSeries->GetData(s)) 
					barHeight = (tmpSeries->GetData(s) * 1.00) * dataScale; 
				else barHeight = tickRange * dataScale; 
				if(m_DrawMode==RELATIVEMODE) 
				{ 
					barHeight =(tmpSeries->GetData(s)*1.00)*m_nUnits/(tickSpace * 1.00); 
				if(tmpSeries->GetData(s)>=tickRange) 
					barHeight=yAxisHeight;//警告,此时数据可能越界,截断越界高度 
 
				} 
				barL = tickXLocation - (dataPlotSize / 2) + (s * barWidth); 
				barT = yApexPoint - barHeight; 
				barR = barL + barWidth; 
				barB = yApexPoint; 
				 
				COLORREF barColor; 
				barColor = GetColor(s); 
				CBrush brush (barColor); 
				CBrush* pOldBrush; 
				pOldBrush = pDC->SelectObject(&brush); 
				pDC->Rectangle(barL, barT, barR, barB); 
				pDC->SelectObject(pOldBrush); 
			} 
		} 
	} 
 
	if(graphType == 1)  //line 
	{ 
		int lastXLoc, lastYLoc; 
		BOOL samsaratag=FALSE;//用于绘制线段标题时其位置的轮换 
		 
		int Count=graphSeries->GetCount(); 
		POINT linepoints[10][50];//储存所有顶点 
		for (int i=0;i<10;i++) 
			for(int j=0;j<50;j++) 
			{ 
				linepoints[i][j].x=0; 
				linepoints[i][j].y=0; 
			} 
 
		for(int s = 0; s < seriesSize; s++) 
		{ 
			//determine width of barchart data blocks 
			  
			if(graphHasLegend) 
				seriesSpace = (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
			else 
				seriesSpace = xAxisWidth / graphSeries->GetCount(); 
			barWidth = (seriesSpace * .6) / seriesSize; 
			dataPlotSize = seriesSize * barWidth; 
 
			pos = graphSeries->GetHeadPosition(); 
             
			for(int x = 1; x <= graphSeries->GetCount(); x++) 
			{ 
				tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
				tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2)); 
				 
				barHeight = 0.00; 
				//to allow scalability (height may be less than tickRange) 
				double dataScale = 0.00; 
				if(tickRange > yAxisHeight) 
					dataScale = (yAxisHeight * 1.00) / (tickRange * 1.00); 
				else dataScale = 1; 
				barHeight = (tmpSeries->GetData(s) * 1.00) * dataScale; 
                if(m_DrawMode==RELATIVEMODE) 
				{ 
                     barHeight = (tmpSeries->GetData(s)*1.00)*m_nUnits/(tickSpace * 1.00); 
					 if(tmpSeries->GetData(s)>=tickRange) 
					    barHeight=yAxisHeight;//警告,此时数据可能越界,截断越界高度 
				} 
				int yDataLocation = yApexPoint - barHeight; 
				 
				//now have x and y location of center of ellipse 
				COLORREF barColor; 
				barColor = GetColor(s); 
				CBrush brush (barColor); 
				CBrush* pOldBrush; 
				pOldBrush = pDC->SelectObject(&brush); 
				//draw line back to last data member 
				if(x > 1) 
				{ 
					CPen* pOldPen; 
					CPen linePen (PS_SOLID, 1, barColor); 
					pOldPen = pDC->SelectObject(&linePen); 
					pDC->MoveTo(lastXLoc + 2, lastYLoc - 1); 
					pDC->LineTo(tickXLocation - 3, yDataLocation - 1); 
					 
                    linepoints[s][x-1].x =tickXLocation; 
                    linepoints[s][x-1].y =yDataLocation; 
					if(x==graphSeries->GetCount()) 
					{//以下为调整linetitle,避免它们互相重叠 
						CString title; 
						if (s>0) 
						{ 
							for(int i=0;i<=s-1;i++) 
							{ 
							  if( abs(tickXLocation -linepoints[i][graphSeries->GetCount()-1].x)<30&&abs(yDataLocation -linepoints[i][graphSeries->GetCount()-1].y)<30)//标题间区域至少(30,30) 
							  { 
								samsaratag=TRUE;//需要调整 
								break; 
							  } 
							} 
						} 
 
						if(samsaratag==FALSE)//默认绘在线段的末尾	 
						{ 
							 
							title=CGraphSeries::GetLineTitle(s); 
						    pDC->SetTextColor(barColor); 
							if( CGraphSeries::GetTitleFlag()!=FALSE) 
						    pDC->TextOut(tickXLocation + 6, yDataLocation - 8,title); 
						 
						} 
						else  
						{ 
						 
						   POINT ptext; 
						   ptext.x =linepoints[s][graphSeries->GetCount()-2].x; 
						   ptext.y =linepoints[s][graphSeries->GetCount()-2].y; 
						   linepoints[s][graphSeries->GetCount()-1].x=ptext.x; 
						   linepoints[s][graphSeries->GetCount()-1].y=ptext.y; 
							for(int i=0;i<=s-1;i++) 
							{ 
							  if( abs(ptext.x  -linepoints[i][graphSeries->GetCount()-1].x)<30&&abs(ptext.y  -linepoints[i][graphSeries->GetCount()-1].y)<30) 
							  { 
								 
								      
								      BOOL exitflag=TRUE; 
									  ptext.x =linepoints[s][graphSeries->GetCount()-3-i].x; 
									  ptext.y =linepoints[s][graphSeries->GetCount()-3-i].y; 
								   
								     linepoints[s][graphSeries->GetCount()-1].x=ptext.x; 
									 linepoints[s][graphSeries->GetCount()-1].y=ptext.y; 
						             for(int j=0;jGetCount()-1].x)<30&&abs(ptext.y  -linepoints[j][graphSeries->GetCount()-1].y)<30) 
						                   exitflag=FALSE; 
										    
									 } 
									 if(exitflag) 
										 break; 
							  } 
							} 
                             
							 
							title=CGraphSeries::GetLineTitle(s); 
						    pDC->SetTextColor(barColor); 
							if( CGraphSeries::GetTitleFlag()!=FALSE) 
						    pDC->TextOut(ptext.x  + 6, ptext.y  - 8,title); 
                            samsaratag=FALSE; 
						     
						 }//调整完毕 
						 
 
 
						 
						 
					} 
				 
					pDC->SelectObject(pOldPen); 
				} 
				//now draw ellipse... 
				pDC->Ellipse(tickXLocation - 3, yDataLocation - 3, 
					tickXLocation + 3, yDataLocation + 3); 
				linepoints[s][x-2].x= lastXLoc = tickXLocation; 
				linepoints[s][x-2].y=lastYLoc = yDataLocation; 
				pDC->SelectObject(pOldBrush); 
			} 
		} 
	} 
 
	if(graphType == 2)  //pie 
	{ 
		double dataSum = 0.00;  //for storing cumulative sum 
		int lastXLocation, lastYLocation; 
		int newXLocation, newYLocation; 
		double percent = 0.00; 
		int degrees; 
		double totalSum = 0.00; 
		int deltaXY; 
		int radius; 
			 
		lastXLocation = 0; 
		lastYLocation = 0; 
 
		//determine width of pie display area 
		if(graphHasLegend) 
			if(((xAxisWidth - legendWidth/* - 10*/) / graphSeries->GetCount()) 
					> (yAxisHeight - 10)) 
				seriesSpace = (yAxisHeight - 10)/* / graphSeries->GetCount()*/; 
			else 
				seriesSpace = (xAxisWidth - legendWidth/* - 10*/) / graphSeries->GetCount(); 
		else 
			if(xAxisWidth > yAxisHeight) 
				seriesSpace = yAxisHeight / graphSeries->GetCount(); 
			else 
				seriesSpace = xAxisWidth / graphSeries->GetCount(); 
		double tmpD = (seriesSpace - /*60*/20)* .8;  //max width of any pie 
		radius = tmpD / 2;   
 
		int centerYPie = (yAxisHeight + 60) / 2; 
		 
		pos = graphSeries->GetHeadPosition(); 
		for(int x = 1; x <= graphSeries->GetCount(); x++) 
		{ 
			tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
			totalSum = 0; 
			for(int s = 0; s < 10; s++) 
				totalSum += tmpSeries->GetData(s); 
			int pieLeft, pieRight; 
			if(graphSeries->GetCount() == 1) 
			{ 
				pieLeft = ((xAxisWidth - legendWidth - 10) / 2) - radius/* - 25*/; 
			} 
			else 
			{ 
				pieLeft = xApexPoint + 15 + (x * /*50*/20) + ((x - 1) * (2 * radius)); 
			} 
			pieRight = pieLeft + (2 * radius); 
			CRect pieRect (pieLeft,  
					centerYPie - radius, 
					pieRight,  
					centerYPie + radius); 
			int centerXPie = pieLeft + radius; 
 
			//plot series label 
			pDC->TextOut(centerXPie - ((tmpSeries->GetLabel().GetLength() * 8) / 2), 
						centerYPie + radius + 15, tmpSeries->GetLabel()); 
			lastXLocation = pieLeft; 
			lastYLocation = centerYPie; 
			 
			dataSum = 0; 
			for(s = 0; s < 10; s++) 
			{ 
				if(tmpSeries->GetData(s) > 0) 
				{ 
					dataSum += tmpSeries->GetData(s); 
					percent = (dataSum * 100) / totalSum; 
					degrees = (360 * percent) / 100; 
 
						//degress / 90 will never exceed 4. 
						//this can tell us the quadrant of destination 
					deltaXY = (degrees * radius) / 90; 
						//deltaXY is change from start point of pie 0 
						//this value is total change.  so if radius is 300 
						//and degrees is 270, delta is 300.  The change  
						//would move both x and y 300 pixels.  For x, 100 
						//to right is center, another 100 to right edge, 
						//100 back to center.  For y, 100 to bottom, 100 
						//back to center, 100 to top. thus gives 270 degree 
						//rotation. 
 
					//determine destination quadrant... 
						//and set newXLocation and newYLocation values... 
					int quadrant = degrees / 90;  //truncates decimal 
					switch(quadrant) 
					{ 
						//in the computations below, the remarked line is 
						//the original computation.  The line above it, for 
						//actual use, is the simplified line, and gives the 
						//exact same result 
						case 0 : newXLocation = pieLeft + deltaXY; 
								 newYLocation = centerYPie + deltaXY; 
								 break; 
						case 1 : newXLocation = pieLeft + deltaXY; 
								 newYLocation = centerYPie + (2 * radius) - deltaXY; 
//								 newYLocation = centerYPie + radius - (deltaXY - radius); 
								 break; 
						case 2 : newXLocation = pieLeft + (4 * radius) - deltaXY; 
//								 newXLocation = pieLeft + (2 * radius) - (deltaXY - (2 * radius)); 
								 newYLocation = centerYPie + (2 * radius) - deltaXY; 
//								 newYLocation = centerYPie - (deltaXY - (2 * radius)); 
								 break; 
						case 3 : newXLocation = pieLeft + (4 * radius) - deltaXY; 
//								 newXLocation = pieLeft + radius - (deltaXY - (3 * radius)); 
								 newYLocation = centerYPie - (4 * radius) + deltaXY; 
//								 newYLocation = centerYPie - radius + (deltaXY - (3 * radius)); 
								 break; 
						case 4 : newXLocation = pieLeft; 
								 newYLocation = centerYPie - 1; 
								 break; 
					} 
					 
					if(s == 0) 
						lastYLocation -= 1; 
 
					CPoint p1 (lastXLocation, lastYLocation); 
					CPoint p2 (newXLocation, newYLocation); 
					COLORREF barColor; 
					barColor = GetColor(s); 
					CBrush brush (barColor); 
					CBrush* pOldBrush; 
					pOldBrush = pDC->SelectObject(&brush); 
					pDC->Pie(pieRect, p1, p2);  
					pDC->SelectObject(pOldBrush); 
 
					lastXLocation = newXLocation; 
					lastYLocation = newYLocation; 
				} 
			} 
			 
		} 
	} 
 
} 
 
void CGraph::SetLegend(int datagroup, CString label) 
{ 
	graphLegend.SetLegendText(datagroup, label); 
	graphHasLegend = TRUE; 
} 
 
void CGraph::DrawLegend(CDC* pDC) 
{ 
	//determine size of legend 
	//12 chars per seriesSize + 6 for spacing (3 top and bottom)  
	//+ 1 set for label title(3+12+6) + rectangle (2 chars) + 3 for final bottom buffer 
	int legendHeight = 23 + (seriesSize * 18) + 3; 
	int legendL, legendT, legendR, legendB; 
	int barL, barT, barR, barB; 
 
	legendT = (maxHeight / 2) - (legendHeight / 2); 
	legendB = legendT + legendHeight; 
	legendR = maxWidth - 5; 
	legendL = legendR - ((graphLegend.GetLegendLength() * (legendFontSize * .66)) + 25); 
		//allows 50 pixels for display of legend bar 45 + 5 space. 
	legendWidth = legendR - legendL; 
 
	pDC->Rectangle(legendL, legendT, legendR, legendB); 
	pDC->TextOut(legendL + (legendWidth / 2) - 24,  
					legendT + 3, "图例");//Legend 
	for(int i = 0; i < seriesSize; i++) 
	{ 
		//top "Legend" text will use 12 + 3 top + 6 bottom (21 total) 
		//each legend label will need 3 chars on top, so the 24 in the offset 
		//each label than uses 12 + 3 below + 3 above next label, so 18 
		// in the i * offset.   
		CFont legendFont; 
		legendFont.CreateFont(legendFontSize, 0, 0, 0, 700, FALSE, FALSE, 0, 
			ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
		CFont* pOldFont = (CFont*) pDC->SelectObject(&legendFont); 
 
		pDC->TextOut(legendL + 5, legendT + 24 + (i * 18), graphLegend.GetLegendText(i)); 
 
		pDC->SelectObject(pOldFont); 
 
		//draw bar 
		COLORREF barColor; 
		barColor = GetColor(i); 
		CBrush brush (barColor); 
		CBrush* pOldBrush; 
		pOldBrush = pDC->SelectObject(&brush); 
 
		barL = legendL + 5 + (graphLegend.GetLegendText(i).GetLength() * 6) + 5; 
		barT = legendT + 24 + (i * 18) + 1, graphLegend.GetLegendText(i); 
		barR = legendR - 5; 
		barB = barT + 12; 
		pDC->Rectangle(barL, barT, barR, barB); 
 
		pDC->SelectObject(pOldBrush); 
 
	 
	} 
 
} 
 
void CGraph::SetGraphTitle(CString title) 
{ 
	graphTitle = title; 
} 
 
void CGraph::SetXTickFontSize(int size) 
{ 
	xTickFontSize = size; 
} 
 
void CGraph::SetYTickFontSize(int size) 
{ 
	yTickFontSize = size; 
} 
 
void CGraph::SetLegendFontSize(int size) 
{ 
	legendFontSize = size; 
} 
 
 
int CGraph::PrintGraph(CDC* pDC, CPrintInfo* pInfo) 
{ 
	CString		str; 
	char		tmpStr[10]; 
	CFont		graphTitleFont, textFont; 
	TEXTMETRIC	tm; 
	CString tickLabel; 
 
    //titleFont is 24 point, bold 
	graphTitleFont.CreateFont(-480, 0, 0, 0, 700, FALSE, FALSE, 0, ANSI_CHARSET, 
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH || FF_ROMAN, 
			"Times New Roman"); 
 
	//textFont is 12 point 
	textFont.CreateFont(-240, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, 
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH || FF_ROMAN, 
			"Times New Roman"); 
 
	pDC->SetMapMode(MM_TWIPS); 
	if(pInfo->m_nCurPage == 1) 
	{ 
		CFont* pOldFont = (CFont*) pDC->SelectObject(&graphTitleFont); 
		pDC->GetTextMetrics(&tm); 
 
		int charWidth = tm.tmMaxCharWidth; 
		//next line is centered....trust me !! 
		pDC->TextOut(((graphTitle.GetLength() / 2) * charWidth) - (leftMargin / 2), pGraphT, graphTitle); 
 
		pDC->SelectObject(pOldFont); 
		 
		//draw legend 
		if(graphHasLegend) 
			PrintLegend(pDC); 
 
		maxHeight = pGraphT - pGraphB;  
		maxWidth = pGraphR - pGraphL - 2200;  
		if(graphType == 2)  //pie 
		{ 
 
			//since pie does not have axis lines, set to full size minus 5 on each side 
			//these are needed for legend to plot itself 
			xAxisWidth = pGraphR - pGraphL - legendWidth - 200; 
			yAxisHeight = maxHeight - 200;  //100 buffer and 200 for title 
			xApexPoint = pGraphL + 1500; 
			yApexPoint = pGraphB + maxHeight + 500; 
		} 
		else 
		{ 
			 
			tickLabel.Format("%d", tickRange); 
 
			//determine axis specifications  
			xApexPoint = pGraphL + 350 + (tickLabel.GetLength() * 240);  
 
			yApexPoint = pGraphB + 500;		 
			yAxisHeight = maxHeight + (pGraphB - yApexPoint) - 1000; 
			xAxisWidth = pGraphR - legendWidth - xApexPoint; 
		} 
 
		if(graphType != 2)  //pie 
		{ 
			//draw axis lines 
			PrintAxis(pDC); 
		} 
 
		//draw series data and labels 
		PrintSeries(pDC); 
 
	}  //end printing for Page 1     
	 
	//update pGraphB value to pad some space below the graph 
	pGraphB -= 90; 
	return pGraphB; 
} 
 
 
void CGraph::PrintLegend(CDC *pDC) 
{ 
	CFont		legendLabelFont, legendTitleFont; 
	TEXTMETRIC	tm; 
 
	int legendL, legendT, legendR, legendB; 
	int barL, barT, barR, barB; 
 
	//legendLabelFont is 10 point 
	legendLabelFont.CreateFont(-200, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, 
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH || FF_ROMAN, 
			"Times New Roman"); 
 
	//legendTitleFont is 12 point 
	legendTitleFont.CreateFont(-240, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, 
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH || FF_ROMAN, 
			"Times New Roman"); 
 
//	pDC->Rectangle(pGraphL, pGraphT, pGraphR, pGraphB); 
	legendT = (((pGraphB - pGraphT) / 2) + pGraphT) + (((seriesSize + 1) / 2) * 240 + 300); 
	legendB = (((pGraphB - pGraphT) / 2) + pGraphT) - (((seriesSize + 1) / 2) * 240 + 300); 
	legendR = pGraphR - 50; 
	legendL = legendR - ((legendR - pGraphL) * .15); 
	legendWidth = legendR - legendL; 
 
	pDC->Rectangle(legendL, legendT, legendR, legendB); 
	CFont* pOldFont = (CFont*) pDC->SelectObject(&legendTitleFont); 
	pDC->GetTextMetrics(&tm); 
	pDC->TextOut(legendL + (legendWidth / 2) - 360,  
					legendT - 160, "图例");//Legend 
	pDC->SelectObject(pOldFont); 
	for(int i = 0; i < seriesSize; i++) 
	{ 
		pOldFont = (CFont*) pDC->SelectObject(&legendLabelFont); 
		pDC->GetTextMetrics(&tm); 
 
		int charWidth = tm.tmMaxCharWidth; 
		int charHeight = tm.tmHeight; 
 
		CString valuStr; 
		valuStr.Format("value %d", i + 1); 
		pDC->TextOut(legendL + 50, legendT - (2 * charHeight) - 120 - (i * charHeight), valuStr); 
		pDC->SelectObject(pOldFont); 
 
		//draw bar 
		COLORREF barColor; 
		barColor = GetColor(i); 
		CBrush brush (barColor); 
		CBrush* pOldBrush; 
		pOldBrush = pDC->SelectObject(&brush); 
 
		barL = legendL + (9 * 120); 
		barT = legendT - (2 * charHeight) - 120 - (i * charHeight) - 60; 
		barR = legendR - 50; 
		barB = barT - 120; 
		pDC->Rectangle(barL, barT, barR, barB); 
 
		pDC->SelectObject(pOldBrush); 
	 
	} 
 
} 
 
void CGraph::PrintAxis(CDC *pDC) 
{ 
	pDC->SetTextColor(RGB(0,0,0)); 
 
	//draw y axis 
	pDC->MoveTo(xApexPoint, yApexPoint);   
	pDC->LineTo(xApexPoint, yApexPoint + yAxisHeight - 250); 
 
	//draw x axis 
	pDC->MoveTo(xApexPoint, yApexPoint);   
	if(graphHasLegend) 
		pDC->LineTo(xApexPoint + (xAxisWidth - 500), yApexPoint); 
	else 
		pDC->LineTo(xApexPoint + xAxisWidth - 500, yApexPoint); 
 
	//draw labels 
	CFont sideFont; 
	sideFont.CreateFont(320, 0, 2700, 0, 700, FALSE, FALSE, 0, 
		ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
	CFont* pOldFont = (CFont*) pDC->SelectObject(&sideFont); 
 
	pDC->TextOut(pGraphL + 10, pGraphT - (maxHeight / 2) - ((axisYLabel.GetLength() / 2) * 160), axisYLabel); 
	pDC->SelectObject(pOldFont); 
 
	pDC->TextOut(xApexPoint + (xAxisWidth / 2) - ((axisXLabel.GetLength() / 2) * 19), maxHeight - 5 - 6, axisXLabel); 
 
	//to allow scalability (height may be less than tickRange) 
	double tickScale = 0.00; 
	if(tickRange > yAxisHeight) 
		tickScale = ((yAxisHeight * 1.00) / (tickRange * 1.00)) * (tickSpace * 20); 
	else tickScale = tickSpace * 15; 
 
	//draw y axis ticks 
	for(int y = 1; y <= tickRange / tickSpace; y++)  //no tick at 0 
	{ 
		int tickYLocation = yApexPoint + (y * tickScale); 
 
		pDC->MoveTo(xApexPoint - 75, tickYLocation); 
		pDC->LineTo(xApexPoint + 75, tickYLocation); 
 
		//draw tick label 
		CString tickLabel; 
		tickLabel.Format("%d", y * tickSpace); 
 
		CFont yFont; 
		yFont.CreateFont(yTickFontSize * 20, 0, 0, 0, 700, FALSE, FALSE, 0, 
			ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
		pOldFont = (CFont*) pDC->SelectObject(&yFont); 
 
		pDC->TextOut(xApexPoint - 150 - (tickLabel.GetLength() * (yTickFontSize / 2) * 20), tickYLocation + 120, tickLabel); 
 
		pDC->SelectObject(pOldFont); 
 
	} 
 
	//draw X axis tick marks 
	POSITION pos; 
	pos = graphSeries->GetHeadPosition(); 
	CGraphSeries* tmpSeries; 
	for(int x = 1; x <= graphSeries->GetCount(); x++) 
	{ 
		tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
		int seriesSpace; 
		int tickXLocation; 
		if(graphHasLegend) 
			seriesSpace= (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
		else 
			seriesSpace= xAxisWidth / graphSeries->GetCount(); 
		tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2)); 
 
		pDC->MoveTo(tickXLocation,yApexPoint - 75); 
		pDC->LineTo(tickXLocation,yApexPoint + 75); 
 
		//draw tick label 
		CString tickLabel; 
		tickLabel = tmpSeries->GetLabel(); 
		CFont sideFont2; 
		sideFont2.CreateFont(xTickFontSize * 20, 0, ((360 - xAxisAlign) * 10), 0, 700, FALSE, FALSE, 0, 
			ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
			DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
		pOldFont = (CFont*) pDC->SelectObject(&sideFont2); 
		if(!xAxisAlign)  //horizontal 
			pDC->TextOut(tickXLocation - ((tickLabel.GetLength() / 2) * (xTickFontSize * 10)), yApexPoint - 160, tickLabel); 
		else 
		{ 
			if(xAxisAlign < 180) 
				pDC->TextOut(tickXLocation - ((xTickFontSize * 20) / 2), yApexPoint - (xTickFontSize * 20) - (xAxisLabelLength * ((xTickFontSize * 20) / 2)), tickLabel); 
			else 
				pDC->TextOut(tickXLocation + ((xTickFontSize * 20) / 2), yApexPoint - 160, tickLabel); 
 
		} 
		pDC->SelectObject(pOldFont); 
	} 
 
} 
 
void CGraph::PrintSeries(CDC *pDC) 
{ 
	int barWidth; 
	int dataPlotSize;   //used to plot rects of data 
	int barL, barT, barR, barB; 
	int tickXLocation; 
	int seriesSpace; 
	double barHeight;  //for scalability 
	POSITION pos; 
	CGraphSeries* tmpSeries; 
 
	if(graphType == 0) //bar 
	{ 
		//determine width of barchart data blocks 
		if(graphHasLegend) 
			seriesSpace = (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
		else 
			seriesSpace = xAxisWidth / graphSeries->GetCount(); 
		barWidth = (seriesSpace * .6) / seriesSize; 
		dataPlotSize = seriesSize * barWidth; 
 
		pos = graphSeries->GetHeadPosition(); 
 
		for(int x = 1; x <= graphSeries->GetCount(); x++) 
		{ 
			tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
			double tickScale = 0.00; 
			if(tickRange > yAxisHeight) 
				tickScale = ((yAxisHeight * 1.00) / (tickRange * 1.00)) * (tickSpace * 20); 
			else tickScale = tickSpace * 15; 
			tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2)); 
 
			for(int s = 0; s < seriesSize; s++) 
			{ 
				barHeight = 0.00; 
 
				int axisHeight = tickScale * (tickRange / tickSpace); 
				double barPercent = 0.00; 
				barPercent = (tmpSeries->GetData(s) * 1.00) / (tickRange * 1.00); 
				barHeight = barPercent * axisHeight; 
 
				barL = tickXLocation - (dataPlotSize / 2) + (s * barWidth); 
				barT = yApexPoint + barHeight; 
				barR = barL + barWidth; 
				barB = yApexPoint; 
				 
				COLORREF barColor; 
				barColor = GetColor(s); 
				CBrush brush (barColor); 
				CBrush* pOldBrush; 
				pOldBrush = pDC->SelectObject(&brush); 
				pDC->Rectangle(barL, barT, barR, barB); 
				pDC->SelectObject(pOldBrush); 
			} 
		} 
	} 
 
	if(graphType == 1)  //line 
	{ 
		int lastXLoc, lastYLoc; 
		for(int s = 0; s < seriesSize; s++) 
		{ 
			//determine width of barchart data blocks 
			if(graphHasLegend) 
				seriesSpace = (xAxisWidth - legendWidth - 10) / graphSeries->GetCount(); 
			else 
				seriesSpace = xAxisWidth / graphSeries->GetCount(); 
			barWidth = (seriesSpace * .6) / seriesSize; 
			dataPlotSize = seriesSize * barWidth; 
 
			pos = graphSeries->GetHeadPosition(); 
 
			for(int x = 1; x <= graphSeries->GetCount(); x++) 
			{ 
				tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
 
				tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2)); 
				 
				barHeight = 0.00; 
				//to allow scalability (height may be less than tickRange) 
				double dataScale = 0.00; 
				if(tickRange > yAxisHeight) 
					dataScale = ((yAxisHeight * 1.00) / (tickRange * 1.00)) * (tickSpace * 20); 
				else dataScale = tickSpace * 15.00; 
 
				int axisHeight = dataScale * (tickRange / tickSpace); 
				double barPercent = 0.00; 
				barPercent = (tmpSeries->GetData(s) * 1.00) / (tickRange * 1.00); 
				barHeight = barPercent * axisHeight; 
 
				int yDataLocation = yApexPoint + barHeight; 
				 
				//now have x and y location of center of ellipse 
				COLORREF barColor; 
				barColor = GetColor(s); 
				CBrush brush (barColor); 
				CBrush* pOldBrush; 
				pOldBrush = pDC->SelectObject(&brush); 
				//draw line back to last data member 
				if(x > 1) 
				{ 
					CPen* pOldPen; 
					CPen linePen (PS_SOLID, 1, barColor); 
					pOldPen = pDC->SelectObject(&linePen); 
					pDC->MoveTo(lastXLoc/* - 40*/, lastYLoc/* + 20*/); 
					pDC->LineTo(tickXLocation/* + 60*/, yDataLocation/* + 20*/); 
					pDC->SelectObject(pOldPen); 
				} 
				//now draw ellipse... 
				pDC->Ellipse(tickXLocation + 60, yDataLocation + 60, 
					tickXLocation - 60, yDataLocation - 60); 
				lastXLoc = tickXLocation; 
				lastYLoc = yDataLocation; 
				pDC->SelectObject(pOldBrush); 
			} 
		} 
	} 
 
	if(graphType == 2)  //pie 
	{ 
		double dataSum = 0.00;  //for storing cumulative sum 
		int lastXLocation, lastYLocation; 
		int newXLocation, newYLocation; 
		double percent = 0.00; 
		int degrees; 
		double totalSum = 0.00; 
		int deltaXY; 
		int radius; 
			 
		lastXLocation = 0; 
		lastYLocation = 0; 
 
		//determine width of pie display area 
		if(graphHasLegend) 
			if((xAxisWidth / graphSeries->GetCount()) 
					> (yAxisHeight - 200)) 
				seriesSpace = (yAxisHeight - 200)/* / graphSeries->GetCount()*/; 
			else 
				seriesSpace = xAxisWidth / graphSeries->GetCount(); 
		else 
			if(xAxisWidth > yAxisHeight) 
				seriesSpace = yAxisHeight / graphSeries->GetCount(); 
			else 
				seriesSpace = xAxisWidth / graphSeries->GetCount(); 
		double tmpD = (seriesSpace - 100) * .9;  //max width of any pie 
		radius = tmpD / 2;   
 
		int centerYPie = pGraphT - (yAxisHeight / 2) - 300; 
		 
		pos = graphSeries->GetHeadPosition(); 
		for(int x = 1; x <= graphSeries->GetCount(); x++) 
		{ 
			tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos); 
			totalSum = 0; 
			for(int s = 0; s < 10; s++) 
				totalSum += tmpSeries->GetData(s); 
			int pieLeft, pieRight; 
			if(graphSeries->GetCount() == 1) 
			{ 
				pieLeft = ((xAxisWidth - 200) / 2) - radius; 
			} 
			else 
			{ 
				pieLeft = pGraphL + 15 + (x * 200) + ((x - 1) * (2 * radius)); 
			} 
			pieRight = pieLeft + (2 * radius); 
			CRect pieRect (pieLeft,  
					centerYPie + radius, 
					pieRight,  
					centerYPie - radius); 
			int centerXPie = pieLeft + radius; 
 
			CFont pieFont; 
			pieFont.CreateFont(xTickFontSize * 20, 0, 0, 0, 700, FALSE, FALSE, 0, 
				ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 
				DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial"); 
			CFont* pOldFont = (CFont*) pDC->SelectObject(&pieFont); 
 
			//plot series label 
			pDC->TextOut(centerXPie - ((tmpSeries->GetLabel().GetLength() / 2) * ((xTickFontSize / 2) * 20)), 
						centerYPie - radius - 300, tmpSeries->GetLabel()); 
			pDC->SelectObject(pOldFont); 
 
			lastXLocation = pieLeft; 
			lastYLocation = centerYPie; 
			 
			dataSum = 0; 
			for(s = 0; s < seriesSize; s++) 
			{ 
				if(tmpSeries->GetData(s) > 0) 
				{ 
					dataSum += tmpSeries->GetData(s); 
					percent = (dataSum * 100) / totalSum; 
					degrees = (360 * percent) / 100; 
 
						//degress / 90 will never exceed 4. 
						//this can tell us the quadrant of destination 
					deltaXY = (degrees * radius) / 90; 
						//deltaXY is change from start point of pie 0 
						//this value is total change.  so if radius is 300 
						//and degrees is 270, delta is 300.  The change  
						//would move both x and y 300 pixels.  For x, 100 
						//to right is center, another 100 to right edge, 
						//100 back to center.  For y, 100 to bottom, 100 
						//back to center, 100 to top. thus gives 270 degree 
						//rotation. 
 
					//determine destination quadrant... 
						//and set newXLocation and newYLocation values... 
					int quadrant = degrees / 90;  //truncates decimal 
					switch(quadrant) 
					{ 
						//in the computations below, the remarked line is 
						//the original computation.  The line above it, for 
						//actual use, is the simplified line, and gives the 
						//exact same result 
						case 0 : newXLocation = pieLeft + deltaXY; 
								 newYLocation = centerYPie - deltaXY; 
								 break; 
						case 1 : newXLocation = pieLeft + deltaXY; 
								 newYLocation = centerYPie - (2 * radius) + deltaXY; 
								 break; 
						case 2 : newXLocation = pieLeft + (4 * radius) - deltaXY; 
								 newYLocation = centerYPie - (2 * radius) + deltaXY; 
								 break; 
						case 3 : newXLocation = pieLeft + (4 * radius) - deltaXY; 
								 newYLocation = centerYPie + (4 * radius) - deltaXY; 
								 break; 
						case 4 : newXLocation = pieLeft; 
								 newYLocation = centerYPie + 1; 
								 break; 
					} 
					 
					if(s == 0) 
						lastYLocation -= 1; 
 
					CPoint p1 (lastXLocation, lastYLocation); 
					CPoint p2 (newXLocation, newYLocation); 
					COLORREF barColor; 
					barColor = GetColor(s); 
					CBrush brush (barColor); 
					CBrush* pOldBrush; 
					pOldBrush = pDC->SelectObject(&brush); 
					pDC->Pie(pieRect, p1, p2);  
					pDC->SelectObject(pOldBrush); 
 
					lastXLocation = newXLocation; 
					lastYLocation = newYLocation; 
				} 
			} 
			 
		} 
	} 
} 
 
void CGraph::SetMargins(int top, int bottom, int left, int right, int graphTop) 
{ 
	//page margins for portrait or landscape distinction 
	topMargin = top; 
	bottomMargin = bottom; 
	leftMargin = left; 
	rightMargin = right; 
 
	//set up rectangle area for showing the graph 
	pGraphL = leftMargin; 
	pGraphT = graphTop; 
	pGraphB = pGraphT - 7200; 
	pGraphR = rightMargin; 
 
	if(pGraphB < bottomMargin) 
		pGraphB = bottomMargin;  
 
}