415 lines
13 KiB
MQL5
415 lines
13 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| 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);
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|