383 lines
12 KiB
MQL5
383 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| LineChart.mqh |
| |||
//| Copyright 2000-2025, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include "ChartCanvas.mqh"
| |||
#include <Arrays\ArrayObj.mqh>
| |||
//+------------------------------------------------------------------+
| |||
//| Class CLineChart |
| |||
//| Usage: generates line chart |
| |||
//+------------------------------------------------------------------+
| |||
class CLineChart : public CChartCanvas
| |||
{
| |||
private:
| |||
//--- data
| |||
CArrayObj *m_values;
| |||
//--- adjusted parameters
| |||
bool m_filled;
| |||
| |||
public:
| |||
CLineChart(void);
| |||
~CLineChart(void);
| |||
//--- create
| |||
virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_ARGB_NORMALIZE);
| |||
//--- adjusted parameters
| |||
void Filled(const bool flag=true) { m_filled=flag; }
| |||
//--- set up
| |||
bool SeriesAdd(const double &value[],const string descr="",const uint clr=0);
| |||
bool SeriesInsert(const uint pos,const double &value[],const string descr="",const uint clr=0);
| |||
bool SeriesUpdate(const uint pos,const double &value[],const string descr=NULL,const uint clr=0);
| |||
bool SeriesDelete(const uint pos);
| |||
bool ValueUpdate(const uint series,const uint pos,double value);
| |||
| |||
protected:
| |||
virtual void DrawChart(void);
| |||
virtual void DrawData(const uint index=0);
| |||
| |||
private:
| |||
double CalcArea(const uint index);
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| Constructor |
| |||
//+------------------------------------------------------------------+
| |||
CLineChart::CLineChart(void) : m_filled(false)
| |||
{
| |||
ShowFlags(FLAG_SHOW_LEGEND|FLAGS_SHOW_SCALES|FLAG_SHOW_GRID);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Destructor |
| |||
//+------------------------------------------------------------------+
| |||
CLineChart::~CLineChart(void)
| |||
{
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Create dynamic resource |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
| |||
{
| |||
//--- create object to store data
| |||
if((m_values=new CArrayObj)==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);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Adds data series |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::SeriesAdd(const double &value[],const string descr,const uint clr)
| |||
{
| |||
//--- check
| |||
if(m_data_total==m_max_data)
| |||
return(false);
| |||
//--- add
| |||
CArrayDouble *arr=new CArrayDouble;
| |||
if(!m_values.Add(arr))
| |||
return(false);
| |||
if(!arr.AssignArray(value))
| |||
return(false);
| |||
if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr))
| |||
return(false);
| |||
if(!m_descriptors.Add(descr))
| |||
return(false);
| |||
m_data_total++;
| |||
//--- redraw
| |||
Redraw();
| |||
//--- succeed
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Inserts data series |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::SeriesInsert(const uint pos,const double &value[],const string descr,const uint clr)
| |||
{
| |||
//--- check
| |||
if(m_data_total==m_max_data)
| |||
return(false);
| |||
if(pos>=m_data_total)
| |||
return(false);
| |||
//--- insert
| |||
CArrayDouble *arr=new CArrayDouble;
| |||
if(!m_values.Insert(arr,pos))
| |||
return(false);
| |||
if(!arr.AssignArray(value))
| |||
return(false);
| |||
if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos))
| |||
return(false);
| |||
if(!m_descriptors.Insert(descr,pos))
| |||
return(false);
| |||
m_data_total++;
| |||
//--- redraw
| |||
Redraw();
| |||
//--- succeed
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Updates data series |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::SeriesUpdate(const uint pos,const double &value[],const string descr,const uint clr)
| |||
{
| |||
//--- check
| |||
if(pos>=m_data_total)
| |||
return(false);
| |||
CArrayDouble *data=m_values.At(pos);
| |||
if(data==NULL)
| |||
return(false);
| |||
//--- update
| |||
if(!data.AssignArray(value))
| |||
return(false);
| |||
if(clr!=0 && !m_colors.Update(pos,clr))
| |||
return(false);
| |||
if(descr!=NULL && !m_descriptors.Update(pos,descr))
| |||
return(false);
| |||
//--- redraw
| |||
Redraw();
| |||
//--- succeed
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Deletes data series |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::SeriesDelete(const uint pos)
| |||
{
| |||
//--- check
| |||
if(pos>=m_data_total && m_data_total!=0)
| |||
return(false);
| |||
//--- delete
| |||
if(!m_values.Delete(pos))
| |||
return(false);
| |||
m_data_total--;
| |||
if(!m_colors.Delete(pos))
| |||
return(false);
| |||
if(!m_descriptors.Delete(pos))
| |||
return(false);
| |||
//--- redraw
| |||
Redraw();
| |||
//--- succeed
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Updates element in data series |
| |||
//+------------------------------------------------------------------+
| |||
bool CLineChart::ValueUpdate(const uint series,const uint pos,double value)
| |||
{
| |||
CArrayDouble *data=m_values.At(series);
| |||
//--- check
| |||
if(data==NULL)
| |||
return(false);
| |||
//--- update
| |||
if(!data.Update(pos,value))
| |||
return(false);
| |||
//--- redraw
| |||
Redraw();
| |||
//--- succeed
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Redraws data |
| |||
//+------------------------------------------------------------------+
| |||
void CLineChart::DrawChart(void)
| |||
{
| |||
if(m_filled)
| |||
{
| |||
//--- calculate areas of filling
| |||
double s[];
| |||
ArrayResize(s,m_data_total);
| |||
ArrayInitialize(s,0);
| |||
for(uint i=0;i<m_data_total;i++)
| |||
{
| |||
CArrayDouble *data=m_values.At(i);
| |||
if(data==NULL)
| |||
continue;
| |||
int total=data.Total();
| |||
if(total<=1)
| |||
continue;
| |||
s[i]=CalcArea(i);
| |||
}
| |||
int index=ArrayMaximum(s);
| |||
while(index!=-1 && s[index]!=0.0)
| |||
{
| |||
//--- draw in area descending order
| |||
DrawData(index);
| |||
s[index]=0.0;
| |||
index=ArrayMaximum(s);
| |||
}
| |||
}
| |||
else
| |||
for(uint i=0;i<m_data_total;i++)
| |||
DrawData(i);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Draws lines |
| |||
//+------------------------------------------------------------------+
| |||
void CLineChart::DrawData(const uint index)
| |||
{
| |||
double value=0.0;
| |||
//--- check
| |||
CArrayDouble *data=m_values.At(index);
| |||
if(data==NULL)
| |||
return;
| |||
int total=data.Total();
| |||
if(total<=1)
| |||
return;
| |||
//--- calculate
| |||
int dx=m_data_area.Width()/(total-1);
| |||
int x=m_data_area.left+1;
| |||
int y1=0;
| |||
int y2=(int)(m_y_0-data[0]*m_scale_y);
| |||
//--- draw
| |||
for(int i=1;i<total;i++,x+=dx)
| |||
{
| |||
y1=y2;
| |||
double val=data[i];
| |||
if(val==EMPTY_VALUE)
| |||
continue;
| |||
if(m_accumulative)
| |||
value+=val;
| |||
else
| |||
value=val;
| |||
y2=(int)(m_y_0-value*m_scale_y);
| |||
if(m_filled)
| |||
{
| |||
if((y1>m_y_0 && y2<m_y_0) || (y1<m_y_0 && y2>m_y_0))
| |||
{
| |||
//--- draw two triangles
| |||
int x3;
| |||
if(y1>y2)
| |||
{
| |||
x3=x+dx*(y1-m_y_0)/(y1-y2);
| |||
FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]);
| |||
FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]);
| |||
}
| |||
else
| |||
{
| |||
x3=x+dx*(m_y_0-y1)/(y2-y1);
| |||
FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]);
| |||
FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]);
| |||
}
| |||
continue;
| |||
}
| |||
if(y1<m_y_0 || y2<m_y_0)
| |||
{
| |||
if(y1>y2)
| |||
FillTriangle(x,y1,x+dx,y2,x+dx,y1,(uint)m_colors[index]);
| |||
if(y1<y2)
| |||
{
| |||
FillTriangle(x,y1,x+dx,y2,x,y2,(uint)m_colors[index]);
| |||
y1=y2;
| |||
}
| |||
}
| |||
if(y1>m_y_0 || y2>m_y_0)
| |||
{
| |||
if(y1<y2)
| |||
FillTriangle(x,y1,x+dx,y2,x+dx,y1,(uint)m_colors[index]);
| |||
if(y1>y2)
| |||
{
| |||
FillTriangle(x,y1,x+dx,y2,x,y2,(uint)m_colors[index]);
| |||
y1=y2;
| |||
}
| |||
}
| |||
FillRectangle(x,m_y_0,x+dx,y1,(uint)m_colors[index]);
| |||
}
| |||
else
| |||
LineAA(x,y1,x+dx,y2,(uint)m_colors[index],STYLE_SOLID);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Area of filling |
| |||
//+------------------------------------------------------------------+
| |||
double CLineChart::CalcArea(const uint index)
| |||
{
| |||
double area =0;
| |||
double value=0;
| |||
int dx =100;
| |||
//---
| |||
CArrayDouble *data=m_values.At(index);
| |||
if(data==NULL)
| |||
return(0);
| |||
int total=data.Total();
| |||
if(total<=1)
| |||
return(0);
| |||
int y1=0;
| |||
int y2=(int)(m_y_0-data[0]*m_scale_y);
| |||
for(int i=0;i<total;i++)
| |||
{
| |||
y1=y2;
| |||
double val=data[i];
| |||
if(val==EMPTY_VALUE)
| |||
continue;
| |||
if(m_accumulative)
| |||
value+=val;
| |||
else
| |||
value=val;
| |||
y2=(int)(m_y_0-value*m_scale_y);
| |||
if((y1>m_y_0 && y2<m_y_0) || (y1<m_y_0 && y2>m_y_0))
| |||
{
| |||
//--- line of values crosses the Y axis
| |||
int x;
| |||
if(y1>y2)
| |||
{
| |||
//--- from the bottom up
| |||
x=dx*(y1-m_y_0)/(y1-y2);
| |||
//--- add area of lower triangle
| |||
area+=x*(y1-m_y_0)/2;
| |||
//--- add area of upper triangle
| |||
area+=(dx-x)*(m_y_0-y2)/2;
| |||
}
| |||
else
| |||
{
| |||
//--- from top down
| |||
x=dx*(m_y_0-y1)/(y2-y1);
| |||
//--- add area of upper triangle
| |||
area+=x*(m_y_0-y1)/2;
| |||
//--- add area of lower triangle
| |||
area+=(dx-x)*(y2-m_y_0)/2;
| |||
}
| |||
continue;
| |||
}
| |||
if(y1<m_y_0 || y2<m_y_0)
| |||
{
| |||
//--- both values are greater than zero
| |||
if(y1>y2)
| |||
{
| |||
//--- add area of triangle
| |||
area+=dx*(y1-y2)/2;
| |||
//--- add area of rectangle
| |||
area+=dx*(m_y_0-y2);
| |||
}
| |||
if(y1<y2)
| |||
{
| |||
//--- add area of triangle
| |||
area+=dx*(y2-y1)/2;
| |||
//--- add area of rectangle
| |||
area+=dx*(m_y_0-y1);
| |||
}
| |||
}
| |||
if(y1>m_y_0 || y2>m_y_0)
| |||
{
| |||
//--- both values are less than zero
| |||
if(y1<y2)
| |||
{
| |||
//--- add area of triangle
| |||
area+=dx*(y2-y1)/2;
| |||
//--- add area of rectangle
| |||
area+=dx*(y1-m_y_0);
| |||
}
| |||
if(y1>y2)
| |||
{
| |||
//--- add area of triangle
| |||
area+=dx*(y1-y2)/2;
| |||
//--- add area of rectangle
| |||
area+=dx*(y2-m_y_0);
| |||
}
| |||
}
| |||
}
| |||
//---
| |||
return(area);
| |||
}
| |||
//+------------------------------------------------------------------+
|