葡萄66

导航

饼图基本完成 (附源码) 欢迎大家提建议

     饼图做得差不多了,现在只能做到这个效果了。第一次用GDI+,进度有点慢。 立体饼图是从最下一层往上绘,有多高就绘多少次,效率确实不高。  还有其它工作,以后再改。

     柱1  

现在有个问题:像下边这种情况,不知道标注指示框该怎么处理?

  柱2 

 

下面是源码:

// MyPie.h : header file

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

//饼图数据结构定义

typedef struct _PIEITEM{
    CString m_strItemName; //名称
     int m_nNum;            //数量
     float m_percent;       //百分比(>=0 && <=1)
    COLORREF m_penColor;   //边框颜色
     COLORREF m_brushColor; //填充颜色
}PIEITEM; //饼图项数据结构定义

typedef struct _PIEINFO{
    PIEITEM *m_pPieItem; //饼图数据项数组
    int m_nCount;        //数据项个数
    int m_nPieHeight;    //饼图高度(当m_nPieHeight==1时就为平面饼图,m_nPieHeight>1就为立体饼图)
    int m_nOffset;       //扇形的偏移距离(当m_nOffset==0时,每块扇形是合拢的,m_nOffset>0时,每块扇形就会分开)
}PIEINFO; //饼图数据结构定义



//一个自封装的类,用于实现饼图绘制
class CMyPie : public CWnd
{
protected:
//Data member
	PIEINFO m_pieInfo;  //保存饼图信息

protected:
//Method
	void DrawPie(CDC *pDC,CRect rc,PIEINFO pieInfo); //绘制饼图	
	
// Construction
public:
	CMyPie();

// Attributes
public:

// Operations
public:
       void SetPieInfo(PIEINFO pieInfo);  //设置饼图信息
	void ReleasePieInfo(void); //取消PieInfo的设置(与SetPieInfo成对使用)
// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMyPie)
	//}}AFX_VIRTUAL

public:
	
	virtual ~CMyPie();

	// Generated message map functions
protected:
	//{{AFX_MSG(CMyPie)
	afx_msg void OnPaint();
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

 
 

 

 

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

//MyPie.cpp

 

#define PI 3.1415926535898 

// CMyPie

CMyPie::CMyPie()
{
        m_pieInfo.m_nCount=0;
	m_pieInfo.m_nOffset=0;
	m_pieInfo.m_nPieHeight=0;
	m_pieInfo.m_pPieItem=NULL;
}

CMyPie::~CMyPie()
{
}



void CMyPie::SetPieInfo(PIEINFO pieInfo)
{
    //设置饼图信息
	m_pieInfo.m_nCount=pieInfo.m_nCount;
	m_pieInfo.m_nOffset=pieInfo.m_nOffset;
	m_pieInfo.m_nPieHeight=pieInfo.m_nPieHeight;
	m_pieInfo.m_pPieItem=new PIEITEM[pieInfo.m_nCount];
	if(m_pieInfo.m_pPieItem!=NULL)
        {
		for(int i=0;i<pieInfo.m_nCount;i++)
                {
		        m_pieInfo.m_pPieItem[i].m_brushColor=pieInfo.m_pPieItem[i].m_brushColor;
			m_pieInfo.m_pPieItem[i].m_penColor=pieInfo.m_pPieItem[i].m_penColor;
			m_pieInfo.m_pPieItem[i].m_strItemName=pieInfo.m_pPieItem[i].m_strItemName;
			m_pieInfo.m_pPieItem[i].m_percent=pieInfo.m_pPieItem[i].m_percent;
			m_pieInfo.m_pPieItem[i].m_nNum=pieInfo.m_pPieItem[i].m_nNum;
		}
	}
}


void CMyPie::ReleasePieInfo()
{
    //取消PieInfo的设置(与SetPieInfo成对使用)
	m_pieInfo.m_nCount=0;
	m_pieInfo.m_nOffset=0;
	m_pieInfo.m_nPieHeight=0;
	if(m_pieInfo.m_pPieItem!=NULL)
        {
	    delete [] m_pieInfo.m_pPieItem;
	    m_pieInfo.m_pPieItem=NULL;
	}
}


BEGIN_MESSAGE_MAP(CMyPie, CWnd)//CGDIOperation)
	//{{AFX_MSG_MAP(CMyPie)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyPie message handlers

void CMyPie::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	// TODO: Add your message handler code here
	if(m_pieInfo.m_pPieItem!=NULL&&m_pieInfo.m_nCount>0)
        {
	        CRect rc,rcDraw;
		this->GetClientRect(&rc);
		CDC dcMemory;
		dcMemory.CreateCompatibleDC(&dc); //创建内存设备描述符表
		CBitmap bitmap,*pOldBitmap;
		bitmap.CreateCompatibleBitmap(&dc,rc.Width(),rc.Height());
                pOldBitmap=dcMemory.SelectObject(&bitmap);
		dcMemory.BitBlt(0,0,rc.Width(),rc.Height(),&dc,0,0,SRCCOPY);
		rcDraw=rc;

		//把饼图区域压缩成200*90	
		int a = (rc.Width()-200) / 2;
		int b = (rc.Height()-90) / 2;
		rcDraw.DeflateRect(a,b,a,b);
		

		DrawPie(&dcMemory,rcDraw,m_pieInfo);  //在内存中绘制饼图
	    
		dc.BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),&dcMemory,0,0,SRCCOPY); //将饼图从内存设备描述符表绘制到显示设备描述符表
		dcMemory.SelectObject(pOldBitmap);
		bitmap.DeleteObject();
		dcMemory.DeleteDC();
	}
	// Do not call CWnd::OnPaint() for painting messages
}





//画饼 主体函数
void CMyPie::DrawPie(CDC *pDC,CRect rc,PIEINFO pieInfo)
{
	//此时传进来的rc为圆的外接矩形
	int nX = rc.left;//左上角点
	int nY = rc.top;
	int nWidth = rc.right - rc.left;//宽、高
	int nHeight = rc.bottom - rc.top;
	
	float zdX = nX + nWidth/2.0;//饼中心点坐标
	float zdY = nY + nHeight/2.0;
	
	float jd = 0.0;//已走过扇形度数
	
	Color brushColor;
	Graphics graphics(pDC->m_hDC);	
	graphics.SetSmoothingMode(SmoothingModeAntiAlias);
	SolidBrush theSolidBrush(Color(0,0,0));
	Color penColor;
	Pen thePen(Color(0,0,0),1);	
	
	
	float half,sx,sy,XsectorEnd,YsectorEnd,XsectorStart,YsectorStart;
	
	nY += pieInfo.m_nPieHeight + 1;
	nHeight -= pieInfo.m_nPieHeight;
	
	for (int j=0;j<pieInfo.m_nPieHeight;j++) //高度为nPieHeight,从底往上画nPieHeight次
	{
		
		nY--;
		zdY = nY + nHeight/2.0;
		jd = 0.0;  //已走过扇形度数
		
		
		for(int i=0; i<pieInfo.m_nCount;  jd+=pieInfo.m_pPieItem[i].m_percent * 360, i++)//逐个画扇形
		{			
			
			float a = nWidth/2.0;
			float b = nHeight/2.0;
			float c = tan((jd+pieInfo.m_pPieItem[i].m_percent * 360)*PI/180);
			float d = tan(jd*PI/180);			
			
			//扇形终点
			if ((jd+pieInfo.m_pPieItem[i].m_percent * 360) > 90 && 
                            (jd+pieInfo.m_pPieItem[i].m_percent * 360) < 270 )
				XsectorEnd = -sqrt( a*a * b*b / ( a*a * c*c + b*b ) );
			else
				XsectorEnd = sqrt( a*a * b*b / ( a*a * c*c + b*b ) );
			
			YsectorEnd = zdY +   c * XsectorEnd;
			XsectorEnd += zdX ;
			
			//扇形起点
			if (jd > 90 && jd < 270)
				XsectorStart = -sqrt( a*a * b*b / ( a*a * d*d + b*b ) );
			else
				XsectorStart = sqrt( a*a * b*b / ( a*a * d*d + b*b ) );
			
			YsectorStart =zdY +   d * XsectorStart;
			XsectorStart += zdX;
			
			
			//指示线终点
			half=pieInfo.m_pPieItem[i].m_percent * 360 / 2;
			
			//指示线终点位于原长短轴加40的椭圆上
			a += 40;
			b += 40;
			c = tan((jd+half)*PI/180);
			if ((jd+half) > 90 && (jd+half) < 270 )
				sx = -sqrt( a*a * b*b / ( a*a * c*c + b*b ) );			
			else
				sx = sqrt( a*a * b*b / ( a*a * c*c + b*b ) );		
			
			sy = zdY +   c * sx;
			sx += zdX ;			
			
			
			
			brushColor.SetFromCOLORREF(pieInfo.m_pPieItem[i].m_brushColor); 
                          // theGDIColor = theGDIPlusColor.ToCOLORREF();			
			
			
			theSolidBrush.SetColor(brushColor);	
			
			
			if (pieInfo.m_nPieHeight-1 == j)
				penColor.SetFromCOLORREF(RGB(0xf3,0xf3,0xf3)); //最顶层扇形边缘画灰白色
			else
				penColor.SetFromCOLORREF(pieInfo.m_pPieItem[i].m_penColor); 
			
                        thePen.SetColor(penColor);			
			
			
			
			//扇形向指示线方向偏移 m_nOffset ,得到X、Y轴的偏移量
			float Xoffset = pieInfo.m_nOffset * cos((jd+half)*PI/180);
			float Yoffset = pieInfo.m_nOffset * sin((jd+half)*PI/180);	
			
			graphics.TranslateTransform(Xoffset,Yoffset);			
			
			
			//画扇形
			if (pieInfo.m_pPieItem[i].m_percent > 0)//大于0.0%时才画扇形
			{
				
				graphics.FillPie(&theSolidBrush,
                                                 RectF(nX,nY,nWidth,nHeight),
                                                 jd, 
                                                 pieInfo.m_pPieItem[i].m_percent * 360);
				graphics.DrawPie(&thePen,
                                                 RectF(nX,nY,nWidth,nHeight), 
                                                 jd, 
                                                 pieInfo.m_pPieItem[i].m_percent * 360);
				
				
				if (j == pieInfo.m_nOffset/2 -1)  //在中间层时绘制指示标注
				{
					graphics.DrawLine(&thePen,PointF(zdX,zdY),PointF(sx,sy));//指示线
					
					//输出文字
					CString strItemName = pieInfo.m_pPieItem[i].m_strItemName;
					CString strPercent,strNum;
					float percent = pieInfo.m_pPieItem[i].m_percent * 100;	
					strPercent.Format("%5.2f",percent);
					strNum.Format("%d台",pieInfo.m_pPieItem[i].m_nNum);
					strItemName = strItemName + "\n" + strPercent + "%  " + strNum;
					WCHAR *wchar = strItemName.AllocSysString();
					
					SolidBrush RectangleBrush(Color(150,251,254,156));
					StringFormat stringFormat;//格式化文本输出格式
					stringFormat.SetAlignment(StringAlignmentCenter);//水平居中
					stringFormat.SetLineAlignment(StringAlignmentCenter);//垂直居中
					FontFamily fontFamily(L"Arial");
					Font theFont(&fontFamily,9,FontStyleRegular,UnitPoint);
					
					if (sx <= zdX) //终点在饼中心左边,指示框向左偏
					{
						graphics.DrawLine(&thePen,PointF(sx,sy),PointF(sx-5,sy));
						graphics.FillRectangle(&RectangleBrush,
                                                                       RectF(sx-95,sy-25,90,50));
						graphics.DrawRectangle(&thePen,RectF(sx-95,sy-25,90,50));
						graphics.DrawString(wchar,-1,&theFont,
                                                                    RectF(sx-95,sy-25,90,50),
                                                                    &stringFormat,&theSolidBrush);		
					}
					else//向右偏
					{
						graphics.DrawLine(&thePen,PointF(sx,sy),PointF(sx+5,sy));
						graphics.FillRectangle(&RectangleBrush,RectF(sx+5,sy-25,90,50));
						graphics.DrawRectangle(&thePen,RectF(sx+5,sy-25,90,50));
						graphics.DrawString(wchar,-1,&theFont,RectF(sx+5,sy-25,90,50),
                                                                    &stringFormat,&theSolidBrush);
					}

				}//end 在中间层时绘制指示标注  
			
                            } //end 大于0时画

			//坐标移回去
			graphics.TranslateTransform(-Xoffset,-Yoffset);
			
						
			
		}//end of i
	}//end of j

}

posted on 2011-05-14 00:06  葡萄66  阅读(277)  评论(0编辑  收藏  举报