751 lines
26 KiB
MQL5
751 lines
26 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| FlameCanvas.mqh |
|
|
//| Copyright 2000-2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#include "Canvas.mqh"
|
|
#include <Controls\Defines.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| Gradient descriptors |
|
|
//+------------------------------------------------------------------+
|
|
struct GRADIENT_COLOR
|
|
{
|
|
uint clr; // color in ARGB format
|
|
uint pos; // position of color in percentage of gradient range
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct GRADIENT_SIZE
|
|
{
|
|
uint size; // width of gradient fill in percentage of base fill
|
|
uint pos; // position of color in percentage of gradient length
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Class CFlameCanvas |
|
|
//| Usage: generates flame |
|
|
//+------------------------------------------------------------------+
|
|
class CFlameCanvas : public CCanvas
|
|
{
|
|
private:
|
|
//--- parameters
|
|
uint m_bar_gap;
|
|
uint m_bar_width;
|
|
uint m_chart_scale;
|
|
double m_chart_price_min;
|
|
double m_chart_price_max;
|
|
ENUM_TIMEFRAMES m_timeframe;
|
|
string m_symbol;
|
|
int m_future_bars;
|
|
int m_back_bars;
|
|
int m_rates_total;
|
|
uint m_palette[256]; // flame palette
|
|
uchar m_flame[]; // buffer for calculation of flame
|
|
uint m_time_redraw;
|
|
uint m_delay;
|
|
// bool m_resize_flag;
|
|
// int m_tick_cnt;
|
|
//--- flame parameters
|
|
datetime m_tb1;
|
|
double m_pb1;
|
|
datetime m_te1;
|
|
double m_pe1;
|
|
datetime m_tb2;
|
|
double m_pb2;
|
|
datetime m_te2;
|
|
double m_pe2;
|
|
//--- equation parameters for flame
|
|
int m_cloud_axis[100];
|
|
double m_a1;
|
|
double m_b1;
|
|
double m_a2;
|
|
double m_b2;
|
|
int m_xb1;
|
|
int m_yb1;
|
|
int m_xe1;
|
|
int m_ye1;
|
|
int m_xb2;
|
|
int m_yb2;
|
|
int m_xe2;
|
|
int m_ye2;
|
|
|
|
public:
|
|
CFlameCanvas(void);
|
|
~CFlameCanvas(void);
|
|
//--- create
|
|
bool FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars=0);
|
|
void RatesTotal(const int value);
|
|
//--- setting
|
|
void PaletteSet(uint clr=0xFF0000);
|
|
//--- draw
|
|
void FlameDraw(const double &prices[],const int width,const int lenght);
|
|
void FlameSet(datetime xb1,double yb1,datetime xe1,double ye1,datetime xb2,double yb2,datetime xe2,double ye2);
|
|
//--- event handler
|
|
void ChartEventHandler(const int id,const long &lparam,const double &dparam,const string &sparam);
|
|
|
|
protected:
|
|
bool Resize(void);
|
|
void ChartScale(void);
|
|
void FlameSet(void);
|
|
void CloudDraw(const double &prices[],const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[],const uchar t_level=255,const bool custom_gradient=true);
|
|
void FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[]);
|
|
void GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[]);
|
|
void GradientVerticalLine(int x,int y1,int y2,const GRADIENT_COLOR &gradient[]);
|
|
void GradientVerticalLineMonochrome(int x,int y1,int y2,uint clr1,uint clr2);
|
|
void FlameCreate(void);
|
|
void FlameCalculate(void);
|
|
void Delay(const uint value);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CFlameCanvas::CFlameCanvas(void) : m_bar_gap(16),
|
|
m_bar_width(8),
|
|
m_chart_scale(1),
|
|
m_chart_price_min(0.0),
|
|
m_chart_price_max(0.0),
|
|
m_timeframe(PERIOD_CURRENT),
|
|
m_symbol(NULL),
|
|
m_future_bars(0),
|
|
m_back_bars(0),
|
|
m_rates_total(0),
|
|
m_time_redraw(0),
|
|
m_delay(50),
|
|
m_tb1(0),
|
|
m_pb1(0),
|
|
m_te1(0),
|
|
m_pe1(0),
|
|
m_tb2(0),
|
|
m_pb2(0),
|
|
m_te2(0),
|
|
m_pe2(0)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CFlameCanvas::~CFlameCanvas(void)
|
|
{
|
|
Destroy();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Creates dynamic resource with object |
|
|
//+------------------------------------------------------------------+
|
|
bool CFlameCanvas::FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars)
|
|
{
|
|
//--- get chart parameters
|
|
ChartScale();
|
|
//--- create
|
|
int width =(int)m_bar_gap*(future_bars+back_bars);
|
|
int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
|
|
if(!CreateBitmap(0,0,name,time-back_bars*PeriodSeconds(),m_chart_price_max,width,height,COLOR_FORMAT_ARGB_NORMALIZE))
|
|
return(false);
|
|
ArrayResize(m_flame,width*height);
|
|
//--- save parameters
|
|
m_future_bars=future_bars;
|
|
m_back_bars =back_bars;
|
|
//--- settings
|
|
PaletteSet();
|
|
m_timeframe =Period();
|
|
m_symbol =Symbol();
|
|
//--- succeed
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Resize |
|
|
//+------------------------------------------------------------------+
|
|
bool CFlameCanvas::Resize(void)
|
|
{
|
|
int x,y;
|
|
//--- get limits
|
|
double min=ChartGetDouble(0,CHART_PRICE_MIN);
|
|
double max=ChartGetDouble(0,CHART_PRICE_MAX);
|
|
if(m_chart_price_max!=max)
|
|
{
|
|
//--- move object
|
|
ObjectSetDouble(0,m_objname,OBJPROP_PRICE,0,max);
|
|
}
|
|
//--- check
|
|
if(m_chart_price_min==min && m_chart_price_max==max)
|
|
return(false);
|
|
m_chart_price_min=min;
|
|
m_chart_price_max=max;
|
|
//--- grt size
|
|
ChartTimePriceToXY(0,0,m_tb1,min,x,y);
|
|
int width =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS)-x;
|
|
int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
|
|
//--- resize
|
|
if(width<m_width)
|
|
width=m_width;
|
|
if(width<=0)
|
|
return(false);
|
|
CCanvas::Resize(width,height);
|
|
//--- resize flame buffer
|
|
ArrayResize(m_flame,width*height);
|
|
ArrayInitialize(m_flame,0);
|
|
ArrayInitialize(m_pixels,0);
|
|
//--- restore parameters
|
|
if(m_pb1!=0.0)
|
|
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
|
//--- succeed
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::RatesTotal(const int value)
|
|
{
|
|
if(value==0)
|
|
return;
|
|
if(m_rates_total==0)
|
|
m_rates_total=value;
|
|
else
|
|
{
|
|
if(m_rates_total!=value)
|
|
{
|
|
//--- move object
|
|
ObjectSetInteger(0,m_objname,OBJPROP_TIME,0,
|
|
ObjectGetInteger(0,m_objname,OBJPROP_TIME)+(value-m_rates_total)*PeriodSeconds());
|
|
m_rates_total=value;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Adjusts to the chart scale |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::ChartScale(void)
|
|
{
|
|
m_chart_scale=(uint)ChartGetInteger(0,CHART_SCALE);
|
|
//--- set params
|
|
switch(m_chart_scale)
|
|
{
|
|
case 0:
|
|
m_bar_gap =1;
|
|
m_bar_width=1;
|
|
break;
|
|
case 1:
|
|
m_bar_gap =2;
|
|
m_bar_width=1;
|
|
break;
|
|
case 2:
|
|
m_bar_gap =4;
|
|
m_bar_width=2;
|
|
break;
|
|
case 3:
|
|
m_bar_gap =8;
|
|
m_bar_width=4;
|
|
break;
|
|
case 4:
|
|
m_bar_gap =16;
|
|
m_bar_width=10;
|
|
break;
|
|
case 5:
|
|
m_bar_gap =32;
|
|
m_bar_width=22;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets palette |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::PaletteSet(uint clr)
|
|
{
|
|
//--- create palette
|
|
double g=0,b=0,dg=1.45,db=0.63;
|
|
//---
|
|
for(uint a,i=0;i<256;i++)
|
|
{
|
|
//--- the first 32 values ??of flame are completely transparent
|
|
a=uchar(i<32?0:i-32);
|
|
//--- generate color for the i value of flame
|
|
m_palette[i]=(a<<24)|(uint(255)<<16)|(uint(g+0.5)<<8)|uint(b+0.5);
|
|
//--- increment the color components
|
|
//--- the red color gets gradient due to transparency
|
|
if(i>80) g+=dg;
|
|
if(i>160) b+=db;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Draws the flame |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameDraw(const double &prices[],const int width,const int lenght)
|
|
{
|
|
static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}};
|
|
static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}};
|
|
//--- draw
|
|
CloudDraw(prices,width,lenght,sword,flame);
|
|
//--- copy flame buffer
|
|
FlameCalculate();
|
|
//--- start timer
|
|
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[])
|
|
{
|
|
//--- check
|
|
int total=ArraySize(m_cloud_axis);
|
|
if(total<2)
|
|
return;
|
|
if(total>lenght)
|
|
total=lenght;
|
|
//--- draw
|
|
int xb,xe; // coordinates of the segment
|
|
int ybm,yem; // coordinates of the center line
|
|
int yb1,ye1; // coordinates of the first line
|
|
int yb2,ye2; // coordinates of the second line
|
|
//--- for implementation of variable width
|
|
int w_total=ArraySize(size);
|
|
if(w_total<2)
|
|
return;
|
|
int w_i =0;
|
|
int w_is=(int)size[w_i].pos*total/100;
|
|
int w_ie=(int)size[w_i+1].pos*total/100;
|
|
double w =size[w_i].size*width/100;
|
|
double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
|
//--- draw from left to right
|
|
xb=0;
|
|
ybm=m_cloud_axis[0];
|
|
yb1=ybm-(int)(w/2);
|
|
yb2=ybm+(int)(w/2);
|
|
//--- draw
|
|
for(int i=1;i<total;i++)
|
|
{
|
|
xe=(int)(i*m_bar_gap);
|
|
if(m_cloud_axis[i]==DBL_MAX)
|
|
continue;
|
|
yem=m_cloud_axis[i];
|
|
w+=dw;
|
|
ye1=yem-(int)(w/2);
|
|
ye2=yem+(int)(w/2);
|
|
//--- draw the segment of 'cloud'
|
|
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
|
xb=xe;
|
|
if(xb>=m_width)
|
|
break;
|
|
yb1=ye1;
|
|
yb2=ye2;
|
|
while(i>=w_ie-1 && i!=total-1)
|
|
{
|
|
w_i++;
|
|
w_is=(int)size[w_i].pos*total/100;
|
|
w_ie=(int)size[w_i+1].pos*total/100;
|
|
w =size[w_i].size*width/100;
|
|
if(w_ie==w_is)
|
|
{
|
|
//--- for "instant" resize
|
|
dw=size[w_i+1].size*width/100-w;
|
|
w+=dw;
|
|
ye1=yem-(int)(w/2);
|
|
ye2=yem+(int)(w/2);
|
|
//--- draw the segment of 'cloud'
|
|
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
|
yb1=ye1;
|
|
yb2=ye2;
|
|
}
|
|
else
|
|
{
|
|
dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//--- copy flame buffer
|
|
FlameCalculate();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets parameters of the flame and starts to draw |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameSet(void)
|
|
{
|
|
m_a1=m_bar_gap*((m_ye1-m_yb1)/((double)m_xe1-m_xb1));
|
|
m_a2=m_bar_gap*((m_ye2-m_yb2)/((double)m_xe2-m_xb2));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets parameters of the flame and starts to draw |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameSet(datetime tb1,double pb1,
|
|
datetime te1,double pe1,
|
|
datetime tb2,double pb2,
|
|
datetime te2,double pe2)
|
|
{
|
|
datetime obj_time =(datetime)ObjectGetInteger(0,m_objname,OBJPROP_TIME);
|
|
double obj_price=ObjectGetDouble(0,m_objname,OBJPROP_PRICE);
|
|
int dx,dy;
|
|
//--- save parameters
|
|
m_tb1=tb1;
|
|
m_pb1=pb1;
|
|
m_te1=te1;
|
|
m_pe1=pe1;
|
|
m_tb2=tb2;
|
|
m_pb2=pb2;
|
|
m_te2=te2;
|
|
m_pe2=pe2;
|
|
//--- resize
|
|
Resize();
|
|
//--- convert
|
|
if(ChartTimePriceToXY(0,0,obj_time,obj_price,dx,dy))
|
|
{
|
|
dy=m_yb1;
|
|
if(ChartTimePriceToXY(0,0,tb1,pb1,m_xb1,m_yb1))
|
|
if(ChartTimePriceToXY(0,0,te1,pe1,m_xe1,m_ye1))
|
|
if(ChartTimePriceToXY(0,0,tb2,pb2,m_xb2,m_yb2))
|
|
if(ChartTimePriceToXY(0,0,te2,pe2,m_xe2,m_ye2))
|
|
{
|
|
//--- convert to canvas coordinates
|
|
m_xb1-=dx;
|
|
m_xe1-=dx;
|
|
m_xb2-=dx;
|
|
m_xe2-=dx;
|
|
//---
|
|
FlameSet();
|
|
}
|
|
}
|
|
//--- start timer
|
|
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate array that describes the body of flame |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameCreate(void)
|
|
{
|
|
static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}};
|
|
static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}};
|
|
//---
|
|
double a=rand(); // parameter of line a*x+b
|
|
double b=rand(); // parameter of line a*x+b
|
|
double c=rand(); // parameter of sine c*Sin(d*x)
|
|
double d=rand(); // parameter of sine c*Sin(d*x)
|
|
int w=rand(); // width at the base
|
|
int l=rand(); // length
|
|
//--- normalize
|
|
a=fmod(a,(m_a2-m_a1))+m_a1;
|
|
b=(m_yb1+m_yb2)/2;
|
|
c=fmod(c,20);
|
|
d=fmod(d,3*M_PI)+M_PI;
|
|
//--- shape
|
|
w%=150;
|
|
if(w<10)
|
|
w=10; // but no less than 10
|
|
sword[1].size=w;
|
|
w=rand();
|
|
l%=50;
|
|
sword[1].pos=l+30;
|
|
l=rand();
|
|
//--- sizes
|
|
w=(m_yb2-m_yb1!=0) ? w%(m_yb2-m_yb1) : 10; // proportional to the starting width
|
|
if(w<10)
|
|
w=10; // but no less than 10
|
|
l=l%((m_xe1-m_xb1)/(int)m_bar_gap-20)+20; // proportional to length
|
|
//--- create
|
|
int total=ArraySize(m_cloud_axis);
|
|
for(int i=0;i<total;i++)
|
|
m_cloud_axis[i]=int(a*i+b+c*sin(i/d));
|
|
//--- draw
|
|
FlameDraw(w,l,sword,flame);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates and renders frame |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::FlameCalculate(void)
|
|
{
|
|
//--- calculate new frame
|
|
int c;
|
|
int idx;
|
|
//--- draw body of flame to the right
|
|
for(int x=0,x_tot=m_width-1;x<x_tot;x++)
|
|
{
|
|
//--- separately for y==0
|
|
c=m_flame[x]+m_flame[x+m_width];
|
|
c+=+m_flame[x]+m_flame[x+m_width];
|
|
m_flame[x]=uchar(c/4);
|
|
//---
|
|
for(int y=1,y_tot=m_height-1;y<y_tot;y++)
|
|
{
|
|
idx=y*m_width+x;
|
|
c=m_flame[idx-m_width]+m_flame[idx]+m_flame[idx+m_width];
|
|
idx++;
|
|
c+=m_flame[idx-m_width]+m_flame[idx]+m_flame[idx+m_width];
|
|
m_flame[idx]=uchar(c/6);
|
|
}
|
|
//--- separately for y==m_height-1
|
|
idx=(m_height-1)*m_width+x;
|
|
c=m_flame[idx-m_width]+m_flame[idx];
|
|
idx++;
|
|
c+=m_flame[idx-m_width]+m_flame[idx];
|
|
m_flame[idx]=uchar(c/4);
|
|
}
|
|
//--- move flame to the resource buffer
|
|
for(int y=0;y<m_height;y++)
|
|
{
|
|
for(int x=0;x<m_width;x++)
|
|
{
|
|
idx=y*m_width+x;
|
|
m_pixels[idx]=m_palette[m_flame[idx]];
|
|
}
|
|
}
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Draws "cloud" |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::CloudDraw(const double &prices[],const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[],const uchar t_level,const bool custom_gradient)
|
|
{
|
|
//--- check
|
|
int total=ArraySize(prices);
|
|
if(total<2)
|
|
return;
|
|
if(total>lenght)
|
|
total=lenght;
|
|
//--- draw
|
|
int xb,xe; // coordinates of the segment
|
|
int ybm,yem; // coordinates of the center line
|
|
int yb1,ye1; // coordinates of the first line
|
|
int yb2,ye2; // coordinates of the second line
|
|
int xx;
|
|
//--- for implementation of variable width
|
|
int w_total=ArraySize(size);
|
|
if(w_total<2)
|
|
return;
|
|
int w_i =0;
|
|
int w_is=(int)size[w_i].pos*total/100;
|
|
int w_ie=(int)size[w_i+1].pos*total/100;
|
|
double w =size[w_i].size*width/100;
|
|
double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
|
//--- draw from left to right
|
|
xb=0;
|
|
ChartTimePriceToXY(0,0,0,prices[0],xx,ybm);
|
|
yb1=ybm-(int)(w/2);
|
|
yb2=ybm+(int)(w/2);
|
|
//--- draw
|
|
for(int i=1;i<total;i++)
|
|
{
|
|
xe=(int)(i*m_bar_gap);
|
|
if(prices[i]==DBL_MAX)
|
|
continue;
|
|
ChartTimePriceToXY(0,0,0,prices[i],xx,yem);
|
|
w+=dw;
|
|
ye1=yem-(int)(w/2);
|
|
ye2=yem+(int)(w/2);
|
|
//--- draw the segment of 'cloud'
|
|
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
|
xb=xe;
|
|
if(xb>=m_width)
|
|
break;
|
|
yb1=ye1;
|
|
yb2=ye2;
|
|
while(i>=w_ie-1 && i!=total-1)
|
|
{
|
|
w_i++;
|
|
w_is=(int)size[w_i].pos*total/100;
|
|
w_ie=(int)size[w_i+1].pos*total/100;
|
|
w =size[w_i].size*width/100;
|
|
if(w_ie==w_is)
|
|
{
|
|
//--- for "instant" resize
|
|
dw=size[w_i+1].size*width/100-w;
|
|
w+=dw;
|
|
ye1=yem-(int)(w/2);
|
|
ye2=yem+(int)(w/2);
|
|
//--- draw the segment of 'cloud'
|
|
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
|
yb1=ye1;
|
|
yb2=ye2;
|
|
}
|
|
else
|
|
{
|
|
dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Draws area with vertical fill using specified gradient |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[])
|
|
{
|
|
//--- it is assumed that the colors array has sufficient size and positions are already sorted in ascending order
|
|
//--- get length by X and Y
|
|
int x1 =xb;
|
|
int y1 =yb1;
|
|
int x2 =xb;
|
|
int y2 =yb2;
|
|
int dx =(xe>xb)? xe-xb : xb-xe;
|
|
int dy1=(ye1>yb1)? ye1-yb1 : yb1-ye1;
|
|
int dy2=(ye2>yb2)? ye2-yb2 : yb2-ye2;
|
|
//--- get direction by X and Y
|
|
int sx =(xb<xe)? 1 : -1;
|
|
int sy1=(yb1<ye1)? 1 : -1;
|
|
int sy2=(yb2<ye2)? 1 : -1;
|
|
int er1=dx-dy1;
|
|
int er2=dx-dy2;
|
|
//--- extreme colors
|
|
uint clr_first=gradient[0].clr;
|
|
uint clr_last =gradient[ArraySize(gradient)-1].clr;
|
|
//--- draw the first line
|
|
while(x1!=xe || y1!=ye1)
|
|
{
|
|
//--- calculate coordinates of next pixel of the first line
|
|
if((er1<<1)>-dy1)
|
|
{
|
|
//--- try to change X coordinate of the first line
|
|
//--- draw the second line
|
|
while(x2!=xe || y2!=ye2)
|
|
{
|
|
//--- calculate coordinates of next pixel of the second line
|
|
if((er2<<1)>-dy2)
|
|
{
|
|
//--- try to change X coordinate of the second line
|
|
//--- gradient fill
|
|
GradientVerticalLine(x1,y1,y2,gradient);
|
|
er2-=dy2;
|
|
if(x2!=xe)
|
|
x2+=sx;
|
|
}
|
|
if((er2<<1)<dx)
|
|
{
|
|
er2+=dx;
|
|
if(y2!=ye2)
|
|
y2+=sy2;
|
|
}
|
|
//--- draw the first line
|
|
if(x1!=x2)
|
|
break;
|
|
}
|
|
er1-=dy1;
|
|
if(x1!=xe)
|
|
x1+=sx;
|
|
}
|
|
if((er1<<1)<dx)
|
|
{
|
|
er1+=dx;
|
|
if(y1!=ye1)
|
|
y1+=sy1;
|
|
}
|
|
}
|
|
//--- gradient fill
|
|
GradientVerticalLine(x1,ye1,ye2,gradient);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Draws gradient vertical line |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::GradientVerticalLineMonochrome(int x,int y1,int y2,uint clr1,uint clr2)
|
|
{
|
|
//---
|
|
double dc;
|
|
int dd,dy=y2-y1;
|
|
//--- check
|
|
if(dy==0)
|
|
return;
|
|
//--- extract components from the first color
|
|
uchar clr=(uchar)clr1;
|
|
//--- parameters of pixels iteration
|
|
if(dy>0)
|
|
{
|
|
dd=dy;
|
|
dy=1;
|
|
}
|
|
else
|
|
{
|
|
dd=-dy;
|
|
dy=-1;
|
|
}
|
|
//--- increments for the color components
|
|
dc=(double)((uchar)clr2-clr)/dd;
|
|
//--- draw
|
|
for(int i=0;y1!=y2;i++,y1+=dy)
|
|
{
|
|
int idx=y1*m_width+x;
|
|
//--- check range
|
|
if(idx<0 || idx>=ArraySize(m_flame))
|
|
continue;
|
|
if(x>=0 && x<m_width && y1>=0 && y1<m_height)
|
|
if(m_flame[idx]<(uchar)(clr+dc*i))
|
|
m_flame[idx]=(uchar)(clr+dc*i);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Draws vertical line with specified gradient |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::GradientVerticalLine(int x,int y1,int y2,const GRADIENT_COLOR &gradient[])
|
|
{
|
|
//--- it is assumed that the colors array has sufficient size and positions are already sorted in ascending order
|
|
int total=ArraySize(gradient);
|
|
int dy=y2-y1;
|
|
//--- draw segments
|
|
for(int i=0;i<total-1;i++)
|
|
GradientVerticalLineMonochrome(x,y1+dy*gradient[i].pos/100,y1+dy*gradient[i+1].pos/100,gradient[i].clr,gradient[i+1].clr);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Event handler |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::ChartEventHandler(const int id,const long &lparam,const double &dparam,const string &sparam)
|
|
{
|
|
//--- events filter
|
|
switch(id)
|
|
{
|
|
case CHARTEVENT_CHART_CHANGE:
|
|
//--- handle only chart modification events
|
|
if(m_chart_scale!=(uint)ChartGetInteger(0,CHART_SCALE))
|
|
{
|
|
Delay(20);
|
|
//--- changed horizontal scale
|
|
ChartScale();
|
|
if(m_pb1!=0.0)
|
|
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
|
return;
|
|
}
|
|
if(m_height!=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS))
|
|
{
|
|
//--- changed vertical size
|
|
Delay(20);
|
|
if(m_pb1!=0.0)
|
|
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
|
return;
|
|
}
|
|
if(m_chart_price_min!=ChartGetDouble(0,CHART_PRICE_MIN) ||
|
|
m_chart_price_max!=ChartGetDouble(0,CHART_PRICE_MAX))
|
|
{
|
|
//--- changed vertical scale
|
|
Delay(20);
|
|
if(m_pb1!=0.0)
|
|
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
|
return;
|
|
}
|
|
break;
|
|
//--- organize custom timer
|
|
case CHARTEVENT_CUSTOM+1302:
|
|
//--- time to draw the new frame?
|
|
if(GetTickCount()>m_time_redraw)
|
|
{
|
|
//--- add the body of flame
|
|
FlameCreate();
|
|
//--- draw frame
|
|
FlameCalculate();
|
|
Update();
|
|
//--- calculate time for the next frame
|
|
m_time_redraw=GetTickCount()+m_delay;
|
|
}
|
|
//--- generate next event for custom timer
|
|
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
|
break;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Delay |
|
|
//+------------------------------------------------------------------+
|
|
void CFlameCanvas::Delay(const uint value)
|
|
{
|
|
//--- too small
|
|
if(value<10)
|
|
return;
|
|
//--- start delay
|
|
uint cnt=GetTickCount()+value;
|
|
//--- delay
|
|
while(cnt>=GetTickCount());
|
|
}
|
|
//+------------------------------------------------------------------+
|