//+------------------------------------------------------------------+ //| PieChart.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ChartCanvas.mqh" //+------------------------------------------------------------------+ //| Class CPieChart | //| Usage: generates pie chart | //+------------------------------------------------------------------+ class CPieChart : public CChartCanvas { private: //--- data CArrayDouble *m_values; //--- for draw int m_x0; int m_y0; int m_r; public: CPieChart(void); ~CPieChart(void); //--- create virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); //--- data bool SeriesSet(const double &value[],const string &text[],const uint &clr[]); bool ValueAdd(const double value,const string descr="",const uint clr=0); bool ValueInsert(const uint pos,const double value,const string descr="",const uint clr=0); bool ValueUpdate(const uint pos,const double value,const string descr=NULL,const uint clr=0); bool ValueDelete(const uint pos); protected: virtual void DrawChart(void); void DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr); string LabelMake(const string text,const double value,const bool to_left); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPieChart::CPieChart(void) { uint flags=FLAG_SHOW_LEGEND|FLAG_SHOW_DESCRIPTORS|FLAG_SHOW_VALUE|FLAG_SHOW_PERCENT; AllowedShowFlags(flags); ShowFlags(flags); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPieChart::~CPieChart(void) { } //+------------------------------------------------------------------+ //| Create dynamic resource | //+------------------------------------------------------------------+ bool CPieChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { //--- create object to store data if((m_values=new CArrayDouble)==NULL) return(false); //--- pass responsibility for its destruction to the parent class m_data=m_values; //--- call method of parent class if(!CChartCanvas::Create(name,width,height,clrfmt)) return(false); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Sets displayed parameters | //+------------------------------------------------------------------+ bool CPieChart::SeriesSet(const double &value[],const string &text[],const uint &clr[]) { //--- !!! user is responsible for correct filling of arrays !!! //--- check if(m_values==NULL) return(false); //--- set if(!m_values.AssignArray(value)) return(false); if(!m_descriptors.AssignArray(text)) return(false); if(!m_colors.AssignArray(clr)) return(false); m_data_total=m_values.Total(); //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Adds displayed parameter (to the end) | //+------------------------------------------------------------------+ bool CPieChart::ValueAdd(const double value,const string descr,const uint clr) { //--- check if((value<=0)) return(false); //--- add if(!m_values.Add(value)) return(false); if(!m_descriptors.Add(descr)) return(false); if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr)) return(false); m_data_total++; //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Inserts displayed parameter (to specified position) | //+------------------------------------------------------------------+ bool CPieChart::ValueInsert(const uint pos,const double value,const string descr,const uint clr) { //--- check if((value<=0)) return(false); //--- insert if(!m_values.Insert(value,pos)) return(false); if(!m_descriptors.Insert(descr,pos)) return(false); if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos)) return(false); m_data_total++; //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Updates displayed parameter (in specified position) | //+------------------------------------------------------------------+ bool CPieChart::ValueUpdate(const uint pos,const double value,const string descr,const uint clr) { //--- check if((value<=0)) return(false); //--- update if(!m_values.Update(pos,value)) return(false); if(descr!=NULL && !m_descriptors.Update(pos,descr)) return(false); if(clr!=0 && !m_colors.Update(pos,clr)) return(false); //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Deletes displayed parameter (from specified position) | //+------------------------------------------------------------------+ bool CPieChart::ValueDelete(const uint pos) { //--- delete if(!m_values.Delete(pos)) return(false); m_data_total--; if(!m_descriptors.Delete(pos)) return(false); if(!m_colors.Delete(pos)) return(false); //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Draws | //+------------------------------------------------------------------+ void CPieChart::DrawChart(void) { //--- check if(m_data_total==0) return; //--- variables string text=""; double angle=M_PI*(m_data_offset%360)/180; int width,height; int dw=0; int dh=0; int index; CPoint p0[]; CPoint p1[]; //--- calculate geometry width =(m_data_area.Width()<<3)/10; height=(m_data_area.Height()<<3)/10; if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) { if(IS_SHOW_VALUE) dw=(int)m_max_value_width; else { if(IS_SHOW_PERCENT) dw=TextWidth("100.00%"); } } else { if(IS_SHOW_DESCRIPTORS) { if(IS_SHOW_VALUE) dw=(int)m_max_value_width+TextWidth(" ()"); else { if(IS_SHOW_PERCENT) dw=TextWidth(" (100.00%)"); } dw+=(int)m_max_descr_width; } } //--- pie chart will always be round width -=2*dw+10; height-=20; m_x0=m_data_area.left+(m_data_area.Width()>>1); m_y0=m_data_area.top+(m_data_area.Height()>>1); m_r =(((width>height) ? height : width)>>1); //--- draw pie chart if(ArrayResize(p0,m_data_total+1)==-1) return; if(m_data_total==1) { FillCircle(m_x0,m_y0,m_r,m_colors[0]); Circle(m_x0,m_y0,m_r,m_color_border); } else { Circle(m_x0,m_y0,m_r,m_color_border); for(uint i=0;i=m_x0) { x=p1[i].x+10; y=p1[i].y; index=m_index[i]; text=LabelMake(m_descriptors[index],m_values[index],false); if(text!="") { Line(p0[i].x,p0[i].y,p1[i].x,p1[i].y,m_color_border); Line(p1[i].x,p1[i].y,x,y,m_color_border); TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER); } } if(m_data_total!=m_index_size) { index=(int)m_data_total-1; if(p0[index].x>=m_x0) { x=p1[index].x+10; y=p1[index].y; text=LabelMake("Others",m_others,true); TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER); } else { x=p1[index].x-10; y=p1[index].y; text=LabelMake("Others",m_others,false); TextOut(x-5,y,text,m_color_text,TA_RIGHT|TA_VCENTER); } if(text!="") { Line(p0[index].x,p0[index].y,p1[index].x,p1[index].y,m_color_border); Line(p1[index].x,p1[index].y,x,y,m_color_border); } } ArrayFree(p1); ArrayFree(p0); } //+------------------------------------------------------------------+ //| Draw pie | //+------------------------------------------------------------------+ void CPieChart::DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr) { //--- draw arc Arc(m_x0,m_y0,m_r,m_r,fi3,fi4,p[idx].x,p[idx].y,p[idx+1].x,p[idx+1].y,clr); //--- variables int x3=p[idx].x; int y3=p[idx].y; int x4=p[idx+1].x; int y4=p[idx+1].y; //--- draw radii if(idx==0) Line(m_x0,m_y0,x3,y3,clr); if(idx!=m_data_total-1) Line(m_x0,m_y0,x4,y4,clr); //--- fill double fi=(fi3+fi4)/2; int xf=m_x0+(int)(0.99*m_r*cos(fi)); int yf=m_y0-(int)(0.99*m_r*sin(fi)); Fill(xf,yf,clr); //--- for small pie if(fi4-fi3<=M_PI_4) Line(m_x0,m_y0,xf,yf,clr); } //+------------------------------------------------------------------+ //| Make label for pie | //+------------------------------------------------------------------+ string CPieChart::LabelMake(const string text,const double value,const bool to_left) { string label=""; //--- if(to_left) { if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) { if(IS_SHOW_VALUE) label=DoubleToString(value,2); else { if(IS_SHOW_PERCENT) label=DoubleToString(100*value/m_sum,2)+"%"; } } else { label=text; if(IS_SHOW_VALUE) label+=" ("+DoubleToString(value,2)+")"; else { if(IS_SHOW_PERCENT) label+=" ("+DoubleToString(100*value/m_sum,2)+"%)"; } } } else { if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) { if(IS_SHOW_VALUE) label=DoubleToString(value,2); else { if(IS_SHOW_PERCENT) label=DoubleToString(100*value/m_sum,2)+"%"; } } else { if(IS_SHOW_VALUE) label="("+DoubleToString(value,2)+") "; else { if(IS_SHOW_PERCENT) label="("+DoubleToString(100*value/m_sum,2)+"%) "; } label+=text; } } //--- return(label); } //+------------------------------------------------------------------+