| //+------------------------------------------------------------------+
|
| //| 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_index_size;i++)
|
| {
|
| index=m_index[i];
|
| double val=m_values[index];
|
| double d_a=2*M_PI*val/m_sum;
|
| DrawPie(angle,angle+d_a,i,p0,m_colors[index]);
|
| angle+=d_a;
|
| angle=MathMod(angle,2*M_PI);
|
| }
|
| if(m_data_total!=m_index_size)
|
| DrawPie(angle,angle+2*M_PI*m_others/m_sum,m_index_size,p0,COLOR2RGB(clrBlack));
|
| for(uint i=0;i<=m_index_size;i++)
|
| Line(m_x0,m_y0,p0[i].x,p0[i].y,m_color_border);
|
| Circle(m_x0,m_y0,m_r,m_color_border);
|
| }
|
| //--- draw descriptors
|
| angle=M_PI*(m_data_offset%360)/180;
|
| int r1=(int)round(1.1*m_r);
|
| if(ArrayResize(p1,m_data_total)==-1)
|
| return;
|
| //--- calculations
|
| for(uint i=0;i<m_index_size;i++)
|
| {
|
| index=m_index[i];
|
| angle+=M_PI*m_values[index]/m_sum;
|
| //--- lines
|
| p0[i].x=m_x0+(int)round(m_r*cos(angle));
|
| p0[i].y=m_y0-(int)round(m_r*sin(angle));
|
| p1[i].x=m_x0+(int)round(r1*cos(angle));
|
| p1[i].y=m_y0-(int)round(r1*sin(angle));
|
| angle+=M_PI*m_values[index]/m_sum;
|
| }
|
| if(m_data_total!=m_index_size)
|
| {
|
| index=(int)m_data_total-1;
|
| angle+=M_PI*m_others/m_sum;
|
| p0[index].x=m_x0+(int)round(m_r*cos(angle));
|
| p0[index].y=m_y0-(int)round(m_r*sin(angle));
|
| p1[index].x=m_x0+(int)round(r1*cos(angle));
|
| p1[index].y=m_y0-(int)round(r1*sin(angle));
|
| }
|
| int x,y;
|
| //--- draw descriptors to the left
|
| for(uint i=0;i<m_index_size;i++)
|
| if(p0[i].x<m_x0)
|
| {
|
| x=p1[i].x-10;
|
| y=p1[i].y;
|
| index=m_index[i];
|
| text=LabelMake(m_descriptors[index],m_values[index],true);
|
| 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_RIGHT|TA_VCENTER);
|
| }
|
| }
|
| //--- draw descriptors to the right
|
| for(uint i=0;i<m_index_size;i++)
|
| if(p0[i].x>=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);
|
| }
|
| //+------------------------------------------------------------------+
|