| //+------------------------------------------------------------------+
|
| //| 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());
|
| }
|
| //+------------------------------------------------------------------+
|