饼图基本完成 (附源码) 欢迎大家提建议
饼图做得差不多了,现在只能做到这个效果了。第一次用GDI+,进度有点慢。 立体饼图是从最下一层往上绘,有多高就绘多少次,效率确实不高。 还有其它工作,以后再改。
现在有个问题:像下边这种情况,不知道标注指示框该怎么处理?
下面是源码:
// 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
}


浙公网安备 33010602011771号