Add gitignore

This commit is contained in:
Nkondog Anselme
2021-11-14 05:50:50 +01:00
parent 407a205132
commit f1729bf7fe
333 changed files with 217876 additions and 400 deletions
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
+347
View File
@@ -0,0 +1,347 @@
//+------------------------------------------------------------------+
//| HistogramChart.mqh |
//| Copyright 2009-2017, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "ChartCanvas.mqh"
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Class CHistogramChart |
//| Usage: generates histogram chart |
//+------------------------------------------------------------------+
class CHistogramChart : public CChartCanvas
{
private:
//--- colors
uint m_fill_brush[];
//--- adjusted parameters
bool m_gradient;
uint m_bar_gap;
uint m_bar_min_size;
uint m_bar_border;
//--- data
CArrayObj *m_values;
public:
CHistogramChart(void);
~CHistogramChart(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 Gradient(const bool flag=true) { m_gradient=flag; }
void BarGap(const uint value) { m_bar_gap=value; }
void BarMinSize(const uint value) { m_bar_min_size=value; }
void BarBorder(const uint value) { m_bar_border=value; }
//--- data
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 DrawData(const uint idx);
void DrawBar(const int x,const int y,const int w,const int h,const uint clr);
void GradientBrush(const int size,const uint fill_clr);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CHistogramChart::CHistogramChart(void) : m_gradient(true),
m_bar_gap(3),
m_bar_min_size(5),
m_bar_border(0)
{
ShowFlags(FLAG_SHOW_LEGEND|FLAGS_SHOW_SCALES|FLAG_SHOW_GRID);
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CHistogramChart::~CHistogramChart(void)
{
if(ArraySize(m_fill_brush)!=0)
ArrayFree(m_fill_brush);
}
//+------------------------------------------------------------------+
//| Create dynamic resource |
//+------------------------------------------------------------------+
bool CHistogramChart::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 CHistogramChart::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 CHistogramChart::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 CHistogramChart::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 CHistogramChart::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 CHistogramChart::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);
}
//+------------------------------------------------------------------+
//| Draws histogram |
//+------------------------------------------------------------------+
void CHistogramChart::DrawData(const uint idx)
{
double value=0.0;
//--- check
CArrayDouble *data=m_values.At(idx);
if(data==NULL)
return;
int total=data.Total();
if(total==0 || (int)idx>=total)
return;
//--- calculate
int x1=m_data_area.left;
int x2=m_data_area.right;
int dx=(x2-x1)/total;
uint clr=m_colors[idx];
uint w=dx/m_data_total;
if(w<m_bar_min_size)
w=m_bar_min_size;
int x=x1+(int)(m_bar_gap+w*idx);
//--- set font
string fontname;
int fontsize=0;
uint fontflags=0;
uint fontangle=0;
if(IS_SHOW_VALUE)
{
FontGet(fontname,fontsize,fontflags,fontangle);
FontSet(fontname,-10*(w-3),fontflags,900);
}
//--- prepare gradient fill
GradientBrush(w,clr);
//--- draw
for(int i=0;i<total;i++,x+=dx)
{
int y,h;
double val=data[i];
if(val==EMPTY_VALUE)
continue;
if(m_accumulative)
value+=val;
else
value=val;
// int val=(int)(value*m_scale_y);
if(value>0)
{
y=(m_y_0-(int)(value*m_scale_y));
h=m_y_0-y;
}
else
{
y=m_y_0;
h=-(int)(value*m_scale_y);
}
DrawBar(x,y,w,h,clr);
//--- draw text of value
if(IS_SHOW_VALUE)
{
string text =DoubleToString(value,2);
int width=(int)(TextWidth(text)+w);
if(value>0)
{
if(width>y-m_y_max)
TextOut(x+w/2,y+w,text,m_color_text,TA_RIGHT|TA_VCENTER);
else
TextOut(x+w/2,y-w,text,m_color_text,TA_LEFT|TA_VCENTER);
}
else
{
if(width>m_y_min-y-h)
TextOut(x+w/2,y+h-w,text,m_color_text,TA_LEFT|TA_VCENTER);
else
TextOut(x+w/2,y+h+w,text,m_color_text,TA_RIGHT|TA_VCENTER);
}
}
}
if(IS_SHOW_VALUE)
FontSet(fontname,fontsize,fontflags,fontangle);
}
//+------------------------------------------------------------------+
//| Draws bar |
//+------------------------------------------------------------------+
void CHistogramChart::DrawBar(const int x,const int y,const int w,const int h,const uint clr)
{
//--- draw bar
if(!m_gradient || ArraySize(m_fill_brush)<w)
FillRectangle(x+1,y+1,w-x-2,h-y-2,clr);
else
{
for(int i=1;i<h;i++)
ArrayCopy(m_pixels,m_fill_brush,(y+i)*m_width+x+1,0,w);
}
//--- draw bar border
if(m_bar_border!=0)
Rectangle(x,y,x+w-1,y+h-1,m_color_border);
}
//+------------------------------------------------------------------+
//| Creates brush for gradient fill |
//+------------------------------------------------------------------+
void CHistogramChart::GradientBrush(const int size,const uint fill_clr)
{
//--- to prepare gradient fill, we will use Bresenham's circle algorithm
//--- X coordinate - size of the fill,
//--- Y coordinate - color brightness
if(m_gradient)
{
//--- adjust gradient radius if necessary
int r=size;
//--- check
if(r<1)
return;
if(r!=ArrayResize(m_fill_brush,r))
return;
//--- initialize brush
ArrayInitialize(m_fill_brush,m_color_background);
//--- variables
int f =1-r;
int dd_x=1;
int dd_y=-2*r;
int dx =0;
int dy =r;
int i1,i2;
uint clr,dclr;
//---
i1=i2=r>>1;
if((r&1)==0)
i1--;
//--- calculate
while(dy>=dx)
{
clr=fill_clr;
dclr=GETRGB(XRGB((r-dy)*GETRGBR(clr)/r,(r-dy)*GETRGBG(clr)/r,(r-dy)*GETRGBB(clr)/r));
clr-=dclr;
m_fill_brush[i1]=clr;
m_fill_brush[i2]=clr;
//---
if(f>=0)
{
dy--;
dd_y+=2;
f+=dd_y;
}
dx++;
if(--i1<0)
break;
i2++;
dd_x+=2;
f+=dd_x;
}
}
else
ArrayFree(m_fill_brush);
}
//+------------------------------------------------------------------+
+383
View File
@@ -0,0 +1,383 @@
//+------------------------------------------------------------------+
//| LineChart.mqh |
//| Copyright 2009-2017, MetaQuotes Software Corp. |
//| http://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);
}
//+------------------------------------------------------------------+
+414
View File
@@ -0,0 +1,414 @@
//+------------------------------------------------------------------+
//| PieChart.mqh |
//| Copyright 2009-2017, MetaQuotes Software Corp. |
//| http://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);
}
//+------------------------------------------------------------------+
+77
View File
@@ -0,0 +1,77 @@
//+------------------------------------------------------------------+
//| DXBox.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
//---
#include "DXMesh.mqh"
#include "DXMath.mqh"
#include "DXUtils.mqh"
//+------------------------------------------------------------------+
//| 3D Box object |
//+------------------------------------------------------------------+
class CDXBox : public CDXMesh
{
public:
CDXBox();
~CDXBox();
//--- create bon in specified context
bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVector3 &from,const DXVector3 &to);
//--- update box
bool Update(const DXVector3 &from,const DXVector3 &to);
private:
//---
void PrepareVertices(const DXVector3 &from,const DXVector3 &to);
};
//+------------------------------------------------------------------+
//| Class constructor |
//+------------------------------------------------------------------+
void CDXBox::CDXBox() : CDXMesh()
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
void CDXBox::~CDXBox(void)
{
}
//+------------------------------------------------------------------+
//| Create box in specified context |
//+------------------------------------------------------------------+
bool CDXBox::Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVector3 &from,const DXVector3 &to)
{
//--- release previous buffers
Shutdown();
//---
DXVertex vertices[];
uint indices[];
//--- prepare box vertices and indices
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
if(!DXComputeBox(from,to,vertices,indices))
return(false);
for(int i=0; i<ArraySize(vertices); i++)
vertices[i].vcolor=white;
//--- create mesh
return(CDXMesh::Create(dispatcher,buffer_scene,vertices,indices));
}
//+------------------------------------------------------------------+
//| Update box bounds |
//+------------------------------------------------------------------+
bool CDXBox::Update(const DXVector3 &from,const DXVector3 &to)
{
//---
DXVertex vertices[];
uint indices[];
//--- prepare box vertices and indices
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
if(!DXComputeBox(from,to,vertices,indices))
return(false);
for(int i=0; i<ArraySize(vertices); i++)
vertices[i].vcolor=white;
//--- update mesh vertices, indices are the same
return(CDXMesh::VerticesSet(vertices));
}
//+------------------------------------------------------------------+
+92
View File
@@ -0,0 +1,92 @@
//+------------------------------------------------------------------+
//| DXVertexBuffer.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#include "DXHandle.mqh"
//+------------------------------------------------------------------+
//| DX vertex buffer |
//+------------------------------------------------------------------+
class CDXVertexBuffer : public CDXHandleShared
{
public:
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
virtual ~CDXVertexBuffer(void)
{
Shutdown();
}
//+------------------------------------------------------------------+
//| Create vertex buffer in specified context |
//+------------------------------------------------------------------+
template<typename TVertex>
bool Create(int context_handle,const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY)
{
Shutdown();
m_context=context_handle;
m_handle=DXBufferCreate(m_context,DX_BUFFER_VERTEX,vertices,start,count);
return(m_handle!=INVALID_HANDLE);
}
//+------------------------------------------------------------------+
//| Render |
//+------------------------------------------------------------------+
bool Render(uint start=0,uint count=WHOLE_ARRAY)
{
return(DXBufferSet(m_context,m_handle,start,count));
}
//+------------------------------------------------------------------+
//| Shutdown |
//+------------------------------------------------------------------+
void Shutdown(void)
{
//--- relase handle
if(m_handle!=INVALID_HANDLE)
DXRelease(m_handle);
m_handle=INVALID_HANDLE;
}
};
//+------------------------------------------------------------------+
//| DX index buffer |
//+------------------------------------------------------------------+
class CDXIndexBuffer : public CDXHandleShared
{
public:
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
virtual ~CDXIndexBuffer(void)
{
Shutdown();
}
//+------------------------------------------------------------------+
//| Create index buffer in specified context |
//+------------------------------------------------------------------+
bool Create(int context_handle,const uint &indices[],uint start=0,uint count=WHOLE_ARRAY)
{
Shutdown();
m_context=context_handle;
m_handle=DXBufferCreate(m_context,DX_BUFFER_INDEX,indices,start,count);
return(m_handle!=INVALID_HANDLE);
}
//+------------------------------------------------------------------+
//| Render |
//+------------------------------------------------------------------+
bool Render(uint start=0,uint count=WHOLE_ARRAY)
{
return(DXBufferSet(m_context,m_handle,start,count));
}
//+------------------------------------------------------------------+
//| Shutdown |
//+------------------------------------------------------------------+
void Shutdown(void)
{
//--- relase handle
if(m_handle!=INVALID_HANDLE)
DXRelease(m_handle);
m_handle=INVALID_HANDLE;
}
};
//+------------------------------------------------------------------+
Binary file not shown.
+373
View File
@@ -0,0 +1,373 @@
//+------------------------------------------------------------------+
//| DXDispatcher.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
//---
#include "DXObjectBase.mqh"
#include "DXData.mqh"
#include "DXBuffers.mqh"
#include "DXInput.mqh"
#include "DXTexture.mqh"
#include "DXShader.mqh"
//--- default shaders
#resource "Shaders/DefaultShaderVertex.hlsl" as string ExtDefaultShaderVertex;
#resource "Shaders/DefaultShaderPixel.hlsl" as string ExtDefaultShaderPixel;
//+------------------------------------------------------------------+
//| DX dispatcher which holds all resources |
//+------------------------------------------------------------------+
class CDXDispatcher : public CDXObjectBase
{
protected:
CDXObjectBase m_dx_resources; // DX resources list (Textures, Shaders, etc.)
//--- default shaders
CDXShader* m_default_vs;
CDXShader* m_default_ps;
public:
CDXDispatcher(void);
~CDXDispatcher(void);
//--- create/destroy
bool Create(int context);
void Destroy(void);
//--- check resources
void Check(void);
//--- get DX Context Handle
int DXContext(void) const { return(m_context); }
//--- create shaders
CDXShader* ShaderCreateDefault(ENUM_DX_SHADER_TYPE shader_type);
CDXShader* ShaderCreateFromFile(ENUM_DX_SHADER_TYPE shader_type,string path,string entry_point);
CDXShader* ShaderCreateFromSource(ENUM_DX_SHADER_TYPE shader_type,string source,string entry_point);
//--- create buffers
template<typename TVertex>
CDXVertexBuffer* VertexBufferCreate(const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY);
CDXIndexBuffer* IndexBufferCreate(const uint &indicies[],uint start=0,uint count=WHOLE_ARRAY);
//--- create shader inputs
template<typename TInput>
CDXInput* InputCreate(void);
CDXTexture* TextureCreateFromFile(string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0);
CDXTexture* TextureCreateFromData(ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0);
private:
//--- add resource to list
bool ResourceAdd(CDXObjectBase *resource);
//--- check resources
void ResourcesCheck(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CDXDispatcher::CDXDispatcher(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CDXDispatcher::~CDXDispatcher(void)
{
Destroy();
}
//+------------------------------------------------------------------+
//| Create |
//+------------------------------------------------------------------+
bool CDXDispatcher::Create(int context)
{
//--- check if context already exist
if(m_context!=INVALID_HANDLE)
return(false);
//--- save context
m_context=context;
//---
return(true);
}
//+------------------------------------------------------------------+
//| Destroy |
//+------------------------------------------------------------------+
void CDXDispatcher::Destroy(void)
{
//--- release default shaders
if(m_default_ps)
{
m_default_ps.Release();
m_default_ps=NULL;
}
if(m_default_vs)
{
m_default_vs.Release();
m_default_vs=NULL;
}
//--- release and delete all DX resources
while(m_dx_resources.Next())
{
CDXHandleShared* resource=(CDXHandleShared*)m_dx_resources.Next();
resource.Release();
}
//--- forget context
m_context=INVALID_HANDLE;
}
//+------------------------------------------------------------------+
//| Check resources |
//+------------------------------------------------------------------+
void CDXDispatcher::Check(void)
{
//---
CDXHandleShared* resource=(CDXHandleShared*)m_dx_resources.Next();
//--- release and delete all DX resources
while(CheckPointer(resource)!=POINTER_INVALID)
{
CDXHandleShared* next=(CDXHandleShared*)resource.Next();
//--- if references count 1 or less, then only we hold the resource
if(resource.References()<=1)
resource.Release();
//---
resource=next;
}
}
//+------------------------------------------------------------------+
//| Create default shader of specified type |
//+------------------------------------------------------------------+
CDXShader* CDXDispatcher::ShaderCreateDefault(ENUM_DX_SHADER_TYPE shader_type)
{
switch(shader_type)
{
//--- default pixel shader
case DX_SHADER_PIXEL:
{
if(m_default_ps==NULL)
m_default_ps=ShaderCreateFromSource(DX_SHADER_PIXEL,ExtDefaultShaderPixel,"PSMain");
return(m_default_ps);
}
//--- default vertex shader
case DX_SHADER_VERTEX:
{
if(m_default_vs==NULL)
{
m_default_vs=ShaderCreateFromSource(DX_SHADER_VERTEX,ExtDefaultShaderVertex,"VSMain");
if(m_default_vs && !m_default_vs.LayoutSet<DXVertex>())
{
m_default_vs.Release();
m_default_vs=NULL;
}
}
return(m_default_vs);
}
}
//--- return result
return(NULL);
}
//+------------------------------------------------------------------+
//| Create new shader of specified type from file |
//+------------------------------------------------------------------+
CDXShader* CDXDispatcher::ShaderCreateFromFile(ENUM_DX_SHADER_TYPE shader_type,string path,string entry_point)
{
//--- open source file
int file=FileOpen(path,FILE_READ);
if(file==INVALID_HANDLE)
return(NULL);
//--- check file size
uint size=(uint)FileSize(file);
FileClose(file);
if(size>16*1024*1024)
return(NULL);
//--- prepare buffer
char buffer[];
ArrayResize(buffer,size);
//--- read file into buffer
int read=(int)FileLoad(path,buffer);
if(read<=0)
return(NULL);
//--- convert vuffer to string
string source=CharArrayToString(buffer,0,WHOLE_ARRAY,CP_UTF8);
//--- add shader by source
return(ShaderCreateFromSource(shader_type,source,entry_point));
}
//+------------------------------------------------------------------+
//| Create new shader of specified type from source code |
//+------------------------------------------------------------------+
CDXShader* CDXDispatcher::ShaderCreateFromSource(ENUM_DX_SHADER_TYPE shader_type,string source,string entry_point)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new shader
CDXShader* shader=new CDXShader();
if(shader==NULL)
return(NULL);
//--- create shader
if(!shader.Create(m_context,shader_type,source,entry_point))
{
shader.Release();
return(NULL);
}
//--- add shader to resources list
if(!ResourceAdd(shader))
{
shader.Release();
return(NULL);
}
//--- return shader
return(shader);
}
//+------------------------------------------------------------------+
//| Create vertex buffer |
//+------------------------------------------------------------------+
template<typename TVertex>
CDXVertexBuffer* CDXDispatcher::VertexBufferCreate(const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new buffer
CDXVertexBuffer* buffer=new CDXVertexBuffer();
if(buffer==NULL)
return(NULL);
//--- create buffer
if(!buffer.Create(m_context,vertices,start,count))
{
buffer.Release();
return(NULL);
}
//--- add buffer to resources list
if(!ResourceAdd(buffer))
{
buffer.Release();
return(NULL);
}
//--- return buffer
return(buffer);
}
//+------------------------------------------------------------------+
//| Create vertex buffer |
//+------------------------------------------------------------------+
CDXIndexBuffer* CDXDispatcher::IndexBufferCreate(const uint &indicies[],uint start=0,uint count=WHOLE_ARRAY)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new buffer
CDXIndexBuffer* buffer=new CDXIndexBuffer();
if(buffer==NULL)
return(NULL);
//--- create buffer
if(!buffer.Create(m_context,indicies,start,count))
{
buffer.Release();
return(NULL);
}
//--- add buffer to resources list
if(!ResourceAdd(buffer))
{
buffer.Release();
return(NULL);
}
//--- return input buffer
return(buffer);
}
//+------------------------------------------------------------------+
//| Create shader input buffer |
//+------------------------------------------------------------------+
template<typename TInput>
CDXInput* CDXDispatcher::InputCreate(void)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new input buffer
CDXInput* input_buffer=new CDXInput();
if(input_buffer==NULL)
return(NULL);
//--- create buffer
if(!input_buffer.Create<TInput>(m_context))
{
input_buffer.Release();
return(NULL);
}
//--- add buffer to resources list
if(!ResourceAdd(input_buffer))
{
input_buffer.Release();
return(NULL);
}
//--- return shader
return(input_buffer);
}
//+------------------------------------------------------------------+
//| Create texture from bitmap file |
//+------------------------------------------------------------------+
CDXTexture* CDXDispatcher::TextureCreateFromFile(string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new texture
CDXTexture* texture=new CDXTexture();
if(texture==NULL)
return(NULL);
//--- create texture
if(!texture.Create(m_context,path,data_x,data_y,data_width,data_height))
{
texture.Release();
return(NULL);
}
//--- add texture to resources list
if(!ResourceAdd(texture))
{
texture.Release();
return(NULL);
}
//--- return shader
return(texture);
}
//+------------------------------------------------------------------+
//| Create texture from raw data, only 32-bit pixel formats supported|
//+------------------------------------------------------------------+
CDXTexture* CDXDispatcher::TextureCreateFromData(ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
{
//--- check context
if(m_context==INVALID_HANDLE)
return(NULL);
//--- allocate new texture
CDXTexture* texture=new CDXTexture();
if(texture==NULL)
return(NULL);
//--- create texture
if(!texture.Create(m_context,format,width,height,data,data_x,data_y,data_width,data_height))
{
texture.Release();
return(NULL);
}
//--- add texture to resources list
if(!ResourceAdd(texture))
{
texture.Release();
return(NULL);
}
//--- return shader
return(texture);
}
//+------------------------------------------------------------------+
//| Add DX resource |
//+------------------------------------------------------------------+
bool CDXDispatcher::ResourceAdd(CDXObjectBase *resource)
{
//--- add resource
if(!CheckPointer(resource))
return(false);
//---
CDXObjectBase *last=&m_dx_resources;
while(CheckPointer(last.Next())!=POINTER_INVALID)
{
if(last==resource)
return(false);
//---
last=last.Next();
}
//---
resource.Next(NULL);
resource.Prev(last);
last.Next(resource);
return(true);
}
//+------------------------------------------------------------------+
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+375
View File
@@ -0,0 +1,375 @@
//+------------------------------------------------------------------+
//| DXMesh.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#include "DXObject.mqh"
#include "DXDispatcher.mqh"
#include "DXData.mqh"
#include "DXUtils.mqh"
//+------------------------------------------------------------------+
//| DX simple mesh object with default shader |
//+------------------------------------------------------------------+
class CDXMesh : public CDXObject
{
protected:
//--- DX resources
CDXVertexBuffer* m_buffer_vertex;
CDXIndexBuffer* m_buffer_index;
CDXInput* m_buffer_object;
CDXInput* m_buffer_scene;
CDXShader* m_shader_vertex;
CDXShader* m_shader_pixel;
CDXTexture* m_texture;
//--- object data
DXMatrix m_transform_matrix;
DXColor m_diffuse_color;
DXColor m_emission_color;
DXColor m_specular_color;
float m_specular_power;
ENUM_DX_PRIMITIVE_TOPOLOGY m_topology;
public:
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CDXMesh(void):m_buffer_vertex(NULL),m_buffer_index(NULL),m_buffer_object(NULL),m_buffer_scene(NULL),m_shader_vertex(NULL),m_shader_pixel(NULL),m_topology(WRONG_VALUE)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
~CDXMesh(void)
{
Shutdown();
}
//+------------------------------------------------------------------+
//| Create mesh object from vertices and indices list |
//+------------------------------------------------------------------+
bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVertex &vertices[],const uint &indices[],ENUM_DX_PRIMITIVE_TOPOLOGY topology=DX_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
{
Shutdown();
//--- check data
if(buffer_scene==NULL || buffer_scene.Handle()==INVALID_HANDLE ||
ArraySize(vertices)<1 || ArraySize(indices)<1)
return(false);
//--- update context
m_context=dispatcher.DXContext();
//--- initialize default parameters
DXMatrixIdentity(m_transform_matrix);
m_diffuse_color =DXColor(1.0,1.0,1.0,1.0);
m_emission_color=DXColor(0.0,0.0,0.0,0.0);
m_specular_color=DXColor(0.0,0.0,0.0,0.0);
m_specular_power=16.0f;
//--- create object input
m_buffer_object=dispatcher.InputCreate<DXInputObject>();
if(m_buffer_object!=NULL)
{
m_buffer_object.AddRef();
//--- use default vertex shader
m_shader_vertex=dispatcher.ShaderCreateDefault(DX_SHADER_VERTEX);
if(m_shader_vertex!=NULL)
{
m_shader_vertex.AddRef();
//--- use default pixel shader
m_shader_pixel=dispatcher.ShaderCreateDefault(DX_SHADER_PIXEL);
if(m_shader_pixel!=NULL)
{
m_shader_pixel.AddRef();
//--- create vertex buffer
m_buffer_vertex=dispatcher.VertexBufferCreate(vertices);
if(m_buffer_vertex!=NULL)
{
m_buffer_vertex.AddRef();
//--- create index buffer
m_buffer_index=dispatcher.IndexBufferCreate(indices);
if(m_buffer_index!=NULL)
m_buffer_index.AddRef();
}
}
}
}
//--- set object buffer to shaders
if(m_buffer_object==NULL || m_shader_vertex==NULL || m_shader_pixel==NULL || m_buffer_vertex==NULL || m_buffer_index==NULL)
{
Shutdown();
return(false);
}
//--- save scene buffer
m_buffer_scene=buffer_scene;
m_buffer_scene.AddRef();
//--- save topology
m_topology=topology;
//---
return(true);
}
//+------------------------------------------------------------------+
//| Create mesh object from OBJ mesh file |
//+------------------------------------------------------------------+
bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,string obj_path,float scale=1.0f,bool inverse_winding=false)
{
DXVertex vertices[];
uint indices[];
//--- load model
if(!DXLoadObjModel(obj_path,vertices,indices,scale))
return(false);
//--- set white vertices color
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
int count=ArraySize(vertices);
for(int i=0; i<count; i++)
vertices[i].vcolor=white;
//--- inverse winding
if(inverse_winding)
DXInverseWinding(vertices,indices);
//--- create mesh
return(Create(dispatcher,buffer_scene,vertices,indices));
}
//+------------------------------------------------------------------+
//| Update vertices |
//+------------------------------------------------------------------+
bool VerticesSet(const DXVertex &vertices[])
{
//--- check size
if(ArraySize(vertices)<1)
return(false);
//--- create new vertex buffer
if(!m_buffer_vertex.Create(m_context,vertices))
{
Shutdown();
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Update indices |
//+------------------------------------------------------------------+
bool IndicesSet(const uint &indices[])
{
//--- check size
if(ArraySize(indices)<1)
return(false);
//--- create new index buffer
if(!m_buffer_index.Create(m_context,indices))
{
Shutdown();
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Update indices |
//+------------------------------------------------------------------+
bool TopologySet(ENUM_DX_PRIMITIVE_TOPOLOGY topology)
{
m_topology=topology;
//---
return(true);
}
//+------------------------------------------------------------------+
//| Get transform matrix |
//+------------------------------------------------------------------+
void TransformMatrixGet(DXMatrix &mat) const
{
mat=m_transform_matrix;
}
//+------------------------------------------------------------------+
//| Set transform matrix |
//+------------------------------------------------------------------+
void TransformMatrixSet(const DXMatrix &mat)
{
m_transform_matrix=mat;
}
//+------------------------------------------------------------------+
//| Get diffuse color |
//+------------------------------------------------------------------+
void DiffuseColorGet(DXColor &clr) const
{
clr=m_diffuse_color;
}
//+------------------------------------------------------------------+
//| Set diffuse color |
//+------------------------------------------------------------------+
void DiffuseColorSet(const DXColor &clr)
{
m_diffuse_color=clr;
}
//+------------------------------------------------------------------+
//| Get specular color |
//+------------------------------------------------------------------+
void SpecularColorGet(DXColor &clr) const
{
clr=m_specular_color;
}
//+------------------------------------------------------------------+
//| Set specular color |
//+------------------------------------------------------------------+
void SpecularColorSet(const DXColor &clr)
{
m_specular_color=clr;
}
//+------------------------------------------------------------------+
//| Get specular power |
//+------------------------------------------------------------------+
void SpecularPowerGet(float &power) const
{
power=m_specular_power;
}
//+------------------------------------------------------------------+
//| Set specular power |
//+------------------------------------------------------------------+
void SpecularPowerSet(float power)
{
m_specular_power=power;
}
//+------------------------------------------------------------------+
//| Get emission color |
//+------------------------------------------------------------------+
void EmissionColorGet(DXColor &clr) const
{
clr=m_emission_color;
}
//+------------------------------------------------------------------+
//| Set emission color |
//+------------------------------------------------------------------+
void EmissionColorSet(const DXColor &clr)
{
m_emission_color=clr;
}
//+------------------------------------------------------------------+
//| Render mesh |
//+------------------------------------------------------------------+
virtual bool Render(void) override
{
//--- update object buffer
DXInputObject input_data;
DXMatrixTranspose(input_data.transform,m_transform_matrix);
input_data.diffuse_color =m_diffuse_color;
input_data.emission_color=m_emission_color;
input_data.specular_color=m_specular_color;
input_data.specular_power=m_specular_power;
//--- clear shader textures
m_shader_pixel.TexturesClear();
//--- set buffers, shaders, topology and render
if(m_buffer_object.InputSet(input_data))
if(m_buffer_vertex.Render())
if(m_buffer_index.Render())
if(m_shader_vertex.InputSet(0,m_buffer_scene) && m_shader_vertex.InputSet(1,m_buffer_object))
if(m_shader_pixel.InputSet(0,m_buffer_scene) && m_shader_pixel.InputSet(1,m_buffer_object))
if(CheckPointer(m_texture)==POINTER_INVALID || m_shader_pixel.TextureSet(0,m_texture))
if(m_shader_vertex.Render())
if(m_shader_pixel.Render())
if(DXPrimiveTopologySet(m_context,m_topology))
return(DXDrawIndexed(m_context));
//---
return(false);
}
//+------------------------------------------------------------------+
//| Set texture from image file |
//+------------------------------------------------------------------+
bool TextureSet(CDXDispatcher &dispatcher,string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
{
//--- check if texture already exist
if(CheckPointer(m_texture)==POINTER_INVALID)
{
m_texture=dispatcher.TextureCreateFromFile(path,data_x,data_y,data_width,data_height);
if(m_texture==NULL)
return(false);
m_texture.AddRef();
return(true);
}
//--- update texture data
if(!m_texture.Create(m_context,path,data_x,data_y,data_width,data_height))
{
m_texture.Release();
m_texture=NULL;
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Set texture from image data |
//+------------------------------------------------------------------+
bool TextureSet(CDXDispatcher &dispatcher,ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0)
{
//--- check if texture already exist
if(CheckPointer(m_texture)==POINTER_INVALID)
{
m_texture=dispatcher.TextureCreateFromData(format,width,height,data,data_x,data_y,data_width,data_height);
if(m_texture==NULL)
return(false);
m_texture.AddRef();
return(true);
}
//--- update texture data
if(!m_texture.Create(m_context,format,width,height,data,data_x,data_y,data_width,data_height))
{
m_texture.Release();
m_texture=NULL;
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Delete used texture |
//+------------------------------------------------------------------+
void TextureDelete()
{
//--- release texture
if(CheckPointer(m_texture)!=POINTER_INVALID)
{
m_texture.Release();
m_texture=NULL;
}
}
//+------------------------------------------------------------------+
//| Shutdown |
//+------------------------------------------------------------------+
virtual void Shutdown(void)
{
//--- release all dx resources
if(CheckPointer(m_buffer_vertex)!=POINTER_INVALID)
{
m_buffer_vertex.Release();
m_buffer_vertex=NULL;
}
if(CheckPointer(m_buffer_index)!=POINTER_INVALID)
{
m_buffer_index.Release();
m_buffer_index=NULL;
}
if(CheckPointer(m_buffer_object)!=POINTER_INVALID)
{
m_buffer_object.Release();
m_buffer_object=NULL;
}
if(CheckPointer(m_texture)!=POINTER_INVALID)
{
m_texture.Release();
m_texture=NULL;
}
if(CheckPointer(m_buffer_scene)!=POINTER_INVALID)
{
m_buffer_scene.Release();
m_buffer_scene=NULL;
}
if(CheckPointer(m_shader_pixel)!=POINTER_INVALID)
{
m_shader_pixel.Release();
m_shader_pixel=NULL;
}
if(CheckPointer(m_shader_vertex)!=POINTER_INVALID)
{
m_shader_vertex.Release();
m_shader_vertex=NULL;
}
m_topology =WRONG_VALUE;
}
};
//+------------------------------------------------------------------+
Binary file not shown.
Binary file not shown.
Binary file not shown.
+162
View File
@@ -0,0 +1,162 @@
//+------------------------------------------------------------------+
//| DXSurface.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
//---
#include "DXMesh.mqh"
#include "DXMath.mqh"
#include "DXUtils.mqh"
//+------------------------------------------------------------------+
//| 3D Box object |
//+------------------------------------------------------------------+
class CDXSurface : public CDXMesh
{
public:
//--- surface creation flags
enum EN_SURFACE_FLAGS
{
SF_NONE =0x0,
SF_TWO_SIDED =0x1,
SF_USE_NORMALS =0x2,
};
//--- surface color scheme
enum EN_COLOR_SCHEME
{
CS_NONE =0,
CS_JET =1,
CS_COLD_TO_HOT =2,
CS_RED_TO_GREEN=3
};
protected:
uint m_data_width;
uint m_data_height;
uint m_flags;
EN_COLOR_SCHEME m_color_scheme;
public:
CDXSurface();
~CDXSurface();
//--- create bon in specified context
bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,double &data[],uint m_data_widht,uint m_data_height,float data_range,const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,uint flags=SF_NONE,EN_COLOR_SCHEME color_scheme=CS_NONE);
//--- update box
bool Update(double &data[],uint m_data_widht,uint m_data_height,float data_range,const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,uint flags=0,EN_COLOR_SCHEME color_scheme=CS_NONE);
private:
//---
void PrepareColors(DXVertex &vertices[],const DXVector3 &from,const DXVector3 &to);
};
//+------------------------------------------------------------------+
//| Class constructor |
//+------------------------------------------------------------------+
void CDXSurface::CDXSurface() : CDXMesh()
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
void CDXSurface::~CDXSurface(void)
{
}
//+------------------------------------------------------------------+
//| Create box in specified context |
//+------------------------------------------------------------------+
bool CDXSurface::Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,
double &data[],uint data_width,uint data_height,float data_range,
const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,
uint flags=SF_NONE,EN_COLOR_SCHEME color_scheme=CS_NONE)
{
//--- release previous buffers
Shutdown();
//--- save parameters
m_data_width =data_width;
m_data_height =data_height;
m_flags =flags;
m_color_scheme=color_scheme;
//---
DXVertex vertices[];
uint indices[];
//--- prepare surface vertices and indices
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
if(!DXComputeSurface(data,data_width,data_height,data_range,from,to,texture_size,flags&SF_TWO_SIDED,flags&SF_USE_NORMALS,vertices,indices))
return(false);
//--- calculate colors
PrepareColors(vertices,from,to);
//--- create mesh
return(CDXMesh::Create(dispatcher,buffer_scene,vertices,indices));
}
//+------------------------------------------------------------------+
//| Update box bounds |
//+------------------------------------------------------------------+
bool CDXSurface::Update(double &data[],uint data_width,uint data_height,float data_range,
const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,
uint flags=0,EN_COLOR_SCHEME color_scheme=CS_NONE)
{
//---
DXVertex vertices[];
uint indices[];
//--- prepare surface vertices and indices
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
if(!DXComputeSurface(data,data_width,data_height,data_range,from,to,texture_size,flags&SF_TWO_SIDED,flags&SF_USE_NORMALS,vertices,indices))
return(false);
//--- calculate colors
m_color_scheme=color_scheme;
PrepareColors(vertices,from,to);
//--- update vertices
bool res=CDXMesh::VerticesSet(vertices);
//--- do not update indices if vertices relations are the same
if(m_data_width!=data_width || m_data_height!=data_height || (m_flags&SF_TWO_SIDED)!=(flags&SF_TWO_SIDED))
res=res && CDXMesh::IndicesSet(indices);
//--- check result
if(!res)
{
Shutdown();
return(false);
}
//--- save parameters
m_data_width =data_width;
m_data_height =data_height;
m_flags =flags;
//--- success
return(true);
}
//+------------------------------------------------------------------+
//| Update box bounds |
//+------------------------------------------------------------------+
void CDXSurface::PrepareColors(DXVertex &vertices[],const DXVector3 &from,const DXVector3 &to)
{
uint count=ArraySize(vertices);
float scale=1.0f/(fmax(FLT_EPSILON,to.y-from.y));
switch(m_color_scheme)
{
case CS_JET:
{
for(uint i=0; i<count; i++)
DXComputeColorJet((vertices[i].position.y-from.y)*scale,vertices[i].vcolor);
break;
}
case CS_COLD_TO_HOT:
{
for(uint i=0; i<count; i++)
DXComputeColorColdToHot((vertices[i].position.y-from.y)*scale,vertices[i].vcolor);
break;
}
case CS_RED_TO_GREEN:
{
for(uint i=0; i<count; i++)
DXComputeColorRedToGreen((vertices[i].position.y-from.y)*scale,vertices[i].vcolor);
break;
}
default:
{
DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f);
for(uint i=0; i<count; i++)
vertices[i].vcolor=white;
break;
}
}
}
//+------------------------------------------------------------------+
Binary file not shown.
+942
View File
@@ -0,0 +1,942 @@
//+------------------------------------------------------------------+
//| DXUtils.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
//---
#include "DXMath.mqh"
#include "DXData.mqh"
//--- data types in Wavefront OBJ file format
#define OBJ_DATA_UNKNOWN 0
#define OBJ_DATA_V 1
#define OBJ_DATA_VT 2
#define OBJ_DATA_VN 3
#define OBJ_DATA_F 4
//+------------------------------------------------------------------+
//| OBJFaceType |
//+------------------------------------------------------------------+
struct OBJFaceType
{
int total;
int v[4];
int t[4];
int n[4];
};
//+------------------------------------------------------------------+
//| Transforms right to left hand system, or backward |
//| TVertex must have |
//| DXVector4 normal and DXVector2 tcoord members |
//+------------------------------------------------------------------+
template <typename TVertex>
void DXInverseWinding(TVertex &vertices[],uint &indices[])
{
//--- proccess vertices
uint count=ArraySize(vertices);
for(uint i=0; i<count; i++)
{
//--- invert normals
vertices[i].normal.x=-vertices[i].normal.x;
vertices[i].normal.y=-vertices[i].normal.y;
vertices[i].normal.z=-vertices[i].normal.z;
//--- flip texture coordinates
vertices[i].tcoord.x=1.0f-vertices[i].tcoord.x;
}
//--- change indices order
count=ArraySize(indices);
for(uint i=2; i<count; i+=3)
{
uint tmp =indices[i];
indices[i] =indices[i-2];
indices[i-2]=tmp;
}
}
//+------------------------------------------------------------------+
//| Load static 3D models from Maya 2011 |
//| https://en.wikibooks.org/wiki/DirectX/10.0/Direct3D/Loading_Maya |
//+------------------------------------------------------------------+
bool DXLoadObjData(const string filename,DXVector4 &v_positions[],DXVector2 &v_tcoords[],DXVector4 &v_normals[],OBJFaceType &faces[],bool show_debug=false)
{
int total_positions=0;
int total_tcoords=0;
int total_normals=0;
int total_faces=0;
//---
ResetLastError();
int file_handle=FileOpen(filename,FILE_READ|FILE_TXT|FILE_ANSI);
if(file_handle==INVALID_HANDLE)
{
printf("FileOpen %s failed, error code=%d",filename,GetLastError());
return(false);
}
int str_length=0;
string str="";
int total_lines=0;
string str_parsed[];
string str_parsed_faces[];
//---
while(!FileIsEnding(file_handle))
{
str=FileReadString(file_handle,str_length);
total_lines++;
//--- replace two spaces to one
StringReplace(str,"\t"," ");
while(StringFind(str," ",0)!=-1)
StringReplace(str," "," ");
//--- split string to tokens
int total_items=StringSplit(str,' ',str_parsed);
if(total_items<1)
continue;
//--- when examining the file you can ignore every line unless it starts with a "V", "VT", "VN", or "F".
//--- the extra information in the file will not be needed for converting .obj to our file format.
StringToUpper(str_parsed[0]);
//--- parse data type
int data_type=OBJ_DATA_UNKNOWN;
if(str_parsed[0]=="V")
data_type=OBJ_DATA_V;
else
if(str_parsed[0]=="VT")
data_type=OBJ_DATA_VT;
else
if(str_parsed[0]=="VN")
data_type=OBJ_DATA_VN;
else
if(str_parsed[0]=="F")
data_type=OBJ_DATA_F;
//--- proceed data type
switch(data_type)
{
//--- 1. "V" lines are for the vertices, each is listed in X, Y, Z float format.
case OBJ_DATA_V:
{
total_positions++;
if(total_items<4)
{
PrintFormat("obj data error at line=%d: %s",total_lines,str);
FileClose(file_handle);
return(false);
}
ArrayResize(v_positions,total_positions,total_positions+1000);
int idx=total_positions-1;
v_positions[idx].x=(float)StringToDouble(str_parsed[1]);
v_positions[idx].y=(float)StringToDouble(str_parsed[2]);
v_positions[idx].z=(float)StringToDouble(str_parsed[3]);
v_positions[idx].w=1.0f;
//--- debug message
if(show_debug)
printf("posiiton %d: %f %f %f",idx,v_positions[idx].x,v_positions[idx].y,v_positions[idx].z);
//---
break;
}
//--- 2. "VT" lines are for the texture coordinates, they are listed in TU, TV float format.
case OBJ_DATA_VT:
{
total_tcoords++;
if(total_items<3)
{
PrintFormat("obj data error at line=%d: %s",total_lines,str);
FileClose(file_handle);
return(false);
}
ArrayResize(v_tcoords,total_tcoords,total_tcoords+1000);
int idx=total_tcoords-1;
v_tcoords[idx].x=(float)StringToDouble(str_parsed[1]);
v_tcoords[idx].y=(float)StringToDouble(str_parsed[2]);
//--- debug message
if(show_debug)
printf("tcoord %d: %f %f",idx,v_tcoords[idx].x,v_tcoords[idx].y);
//---
break;
}
//--- 3. "VN" lines are for the normal vectors, most of them are duplicated again
//--- since it records them for every vertex in every triangle in the model, they are listed in NX, NY, NZ float format.
case OBJ_DATA_VN:
{
total_normals++;
if(total_items<4)
{
PrintFormat("obj data error at line=%d: %s",total_lines,str);
FileClose(file_handle);
return(false);
}
//---
ArrayResize(v_normals,total_normals,total_normals+1000);
int idx=total_normals-1;
v_normals[idx].x=(float)StringToDouble(str_parsed[1]);
v_normals[idx].y=(float)StringToDouble(str_parsed[2]);
v_normals[idx].z=(float)StringToDouble(str_parsed[3]);
v_normals[idx].w=0.0f;
//--- debug message
if(show_debug)
printf("normal %d: %f %f %f",idx,v_normals[idx].x,v_normals[idx].y,v_normals[idx].z);
//---
break;
}
//--- 4. "F" lines are for each face in the model.
//--- the values listed are indexes into the vertices, texture coordinates, and normal vectors. The format of each face is:
//--- f Vertex1/Texture1/Normal1 Vertex2/Texture2/Normal2 Vertex3/Texture3/Normal3
//--- so a line that says "f 3/13/5 4/14/6 5/15/7" then translates to "Vertex3/Texture13/Normal5 Vertex4/Texture14/Normal6 Vertex5/Texture15/Normal7".
case OBJ_DATA_F:
{
total_faces++;
//--- check size
if(total_items<4)
{
PrintFormat("obj data error at line=%d: %s",total_lines,str);
FileClose(file_handle);
return(false);
}
//---
ArrayResize(faces,total_faces,total_faces+1000);
int idx=total_faces-1;
//--- clear all face indices
for(uint i=0; i<4; i++)
{
faces[idx].v[0]=0;
faces[idx].t[0]=0;
faces[idx].n[0]=0;
}
//--- read indices
faces[idx].total=MathMin(total_items-1,4);
for(int i=0; i<faces[idx].total; i++)
{
int elements=StringSplit(str_parsed[i+1],'/',str_parsed_faces);
if(elements>0)
faces[idx].v[i]=(int)StringToInteger(str_parsed_faces[0]);
if(elements>1)
faces[idx].t[i]=(int)StringToInteger(str_parsed_faces[1]);
if(elements>2)
faces[idx].n[i]=(int)StringToInteger(str_parsed_faces[2]);
}
//--- debug message
if(show_debug)
printf("%face d: %d vertex(%d,%d,%d,%d) texture(%d,%d,%d,%d) normal(%d,%d,%d,%d)",idx+1,
faces[idx].v[0],faces[idx].v[1],faces[idx].v[2],faces[idx].v[3],
faces[idx].t[0],faces[idx].t[1],faces[idx].t[2],faces[idx].t[3],
faces[idx].n[0],faces[idx].n[1],faces[idx].n[2],faces[idx].n[3]);
//---
break;
}
default:
{
break;
}
}
}
//--- close the file
FileClose(file_handle);
//---
if(show_debug)
{
printf("File %s loaded successfully. Total lines: %d",filename,total_lines);
printf("total v_positions=%d",total_positions);
printf("total v_normals=%d",total_normals);
printf("total v_tcoords=%d",total_tcoords);
printf("total faces=%d",total_faces);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Load static 3D models from Maya 2011 |
//| https://en.wikibooks.org/wiki/DirectX/10.0/Direct3D/Loading_Maya |
//| TVertex must have |
//| DXVector4 position, normal and DXVector2 tcoord members |
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXLoadObjModel(const string filename,TVertex &vertices[],uint &indices[],float scale=1.0f)
{
//--- intermediate data arrays
DXVector4 v_positions[];
DXVector2 v_tcoords[];
DXVector4 v_normals[];
OBJFaceType faces[];
//--- load data
if(!DXLoadObjData(filename,v_positions,v_tcoords,v_normals,faces,false))
{
printf("Error loading model data from %s",filename);
return(false);
}
//---
int total_v_positions=ArraySize(v_positions);
int total_v_tcoords =ArraySize(v_tcoords);
int total_v_normals =ArraySize(v_normals);
int total_faces =ArraySize(faces);
//--- check faces
if(total_faces==0)
{
printf("No model data.");
return(false);
}
//--- check consistency of the indices
int vertices_count=0;
int indices_count =0;
bool split_vertices=false;
for(int i=0; i<total_faces; i++)
{
//--- each face have to be 3 or 4 sided
if(faces[i].total<3 || faces[i].total>4)
{
printf("Error in %d face, face vertices count is %d",i,faces[i].total);
return(false);
}
//---
for(int j=0; j<faces[i].total; j++)
{
if(faces[i].v[j]<=0 || faces[i].v[j]>total_v_positions)
{
printf("Error in %d face, %d vertext index is %d. Total posiitons=%d",i,j,faces[i].v[j],total_v_positions);
return(false);
}
if(total_v_tcoords>0)
{
if(faces[i].t[j]<=0 || faces[i].t[j]>total_v_tcoords)
{
printf("Error in %d face, %d tcoord index is %d. Total tcoords=%d",i,j,faces[i].v[j],total_v_positions);
return(false);
}
if(faces[i].t[j]!=faces[i].v[j])
split_vertices=true;
}
if(total_v_normals>0)
{
if(faces[i].n[j]<=0 || faces[i].n[j]>total_v_normals)
{
printf("Error in %d face, %d normal index is %d. Total normals=%d",i,j,faces[i].v[j],total_v_positions);
return(false);
}
if(faces[i].n[j]!=faces[i].v[j])
split_vertices=true;
}
}
//--- calc counts
vertices_count+=faces[i].total;
if(faces[i].total<4)
indices_count+=3;
else
indices_count+=6;
}
printf("Data consistency checked.");
//--- prepare arrays
if(!split_vertices)
vertices_count=total_v_positions;
ArrayResize(vertices,vertices_count);
ArrayResize(indices, indices_count);
int v_idx=0,i_idx=0;
for(int i=0; i<total_faces; i++)
{
for(int j=0; j<faces[i].total; j++)
{
if(!split_vertices)
v_idx=faces[i].v[j]-1;
//--- posiitons
vertices[v_idx].position.x=v_positions[faces[i].v[j]-1].x*scale;
vertices[v_idx].position.y=v_positions[faces[i].v[j]-1].y*scale;
vertices[v_idx].position.z=v_positions[faces[i].v[j]-1].z*scale;
vertices[v_idx].position.w=1.0f;
//--- normal
if(faces[i].n[j])
DXVec4Normalize(vertices[v_idx].normal,v_normals[faces[i].n[j]-1]);
//--- tcoord
if(faces[i].t[j])
vertices[v_idx].tcoord=v_tcoords[faces[i].t[j]-1];
//--- indices
indices[i_idx++]=v_idx;
//--- increment indx
if(split_vertices)
v_idx++;
}
//--- the end of second triangle in 4-sided face
if(faces[i].total==4)
{
indices[i_idx]=indices[i_idx-4];
i_idx++;
indices[i_idx]=indices[i_idx-3];
i_idx++;
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Box |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeBox(const DXVector3 &from,const DXVector3 &to,TVertex &vertices[],uint &indices[])
{
//--- prepare arrays
const int faces=6;
if(ArrayResize(vertices,4*faces)!=4*faces)
return(false);
//--- set indices
uint ind[]= {0,1,2, 2,3,0, 4,5,6, 6,7,4, 8,9,10, 10,11,8, 12,13,14, 14,15,12, 16,17,18, 18,19,16, 20,21,22, 22,23,20};
ArrayResize(indices,0);
if(ArrayCopy(indices,ind)!=ArraySize(ind))
return(false);
//--- prepare boundaries
float left=from.x,right=to.x,bottom=from.y,top=to.y,near=from.z,far=to.z;
if(from.x>to.x)
{
right=from.x;
left= to.x;
}
if(from.y>to.y)
{
top= from.y;
bottom=to.y;
}
if(from.z>to.z)
{
far=from.z;
near=to.z;
}
//--- left face
vertices[0].position=DXVector4(left,top, far, 1.0);
vertices[1].position=DXVector4(left,top, near,1.0);
vertices[2].position=DXVector4(left,bottom,near,1.0);
vertices[3].position=DXVector4(left,bottom,far, 1.0);
for(int i=0; i<4; i++)
vertices[i].normal=DXVector4(-1.0,0.0,0.0,0.0);
//--- right face
vertices[4].position=DXVector4(right,top, near,1.0);
vertices[5].position=DXVector4(right,top, far, 1.0);
vertices[6].position=DXVector4(right,bottom,far, 1.0);
vertices[7].position=DXVector4(right,bottom,near,1.0);
for(int i=4; i<8; i++)
vertices[i].normal=DXVector4(1.0,0.0,0.0,0.0);
//--- front face
vertices[8].position =DXVector4(left, top, near,1.0);
vertices[9].position =DXVector4(right,top, near,1.0);
vertices[10].position=DXVector4(right,bottom,near,1.0);
vertices[11].position=DXVector4(left, bottom,near,1.0);
for(int i=8; i<12; i++)
vertices[i].normal=DXVector4(0.0,0.0,-1.0,0.0);
//--- back face
vertices[12].position=DXVector4(right,top, far,1.0);
vertices[13].position=DXVector4(left, top, far,1.0);
vertices[14].position=DXVector4(left, bottom,far,1.0);
vertices[15].position=DXVector4(right,bottom,far,1.0);
for(int i=12; i<16; i++)
vertices[i].normal=DXVector4(0.0,0.0,1.0,0.0);
//--- top face
vertices[16].position=DXVector4(left, top,far, 1.0);
vertices[17].position=DXVector4(right,top,far, 1.0);
vertices[18].position=DXVector4(right,top,near,1.0);
vertices[19].position=DXVector4(left, top,near,1.0);
for(int i=16; i<20; i++)
vertices[i].normal=DXVector4(0.0,1.0,0.0,0.0);
//--- bottom face
vertices[20].position=DXVector4(left, bottom,near,1.0);
vertices[21].position=DXVector4(right,bottom,near,1.0);
vertices[22].position=DXVector4(right,bottom,far, 1.0);
vertices[23].position=DXVector4(left, bottom,far, 1.0);
for(int i=20; i<24; i++)
vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0);
//--- texture coordinates
for(int i=0; i<faces; i++)
{
vertices[i*4+0].tcoord=DXVector2(0.0f,0.0f);
vertices[i*4+1].tcoord=DXVector2(1.0f,0.0f);
vertices[i*4+2].tcoord=DXVector2(1.0f,1.0f);
vertices[i*4+3].tcoord=DXVector2(0.0f,1.0f);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Sphere |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeSphere(float radius,uint tessellation,TVertex &vertices[],uint &indices[])
{
if(tessellation<3)
tessellation=3;
uint segments_y =tessellation;
uint segments_xz=tessellation*2;
//--- prepare arrays
uint count=(segments_y+1)*(segments_xz+1);
if(ArrayResize(vertices,count)!=count)
return(false);
count=6*segments_y*(segments_xz);
if(ArrayResize(indices,count)!=count)
return(false);
//--- create rings of vertices at progressively higher latitudes.
for(uint i=0,idx=0; i<=segments_y; i++)
{
DXVector2 tcoord=DXVector2(0.0f,1.0f-(float)i/segments_y);
float latitude=(i*DX_PI/segments_y)-DX_PI_DIV2;
float dy =(float)sin(latitude);
float dxz=(float)cos(latitude);
//--- create a single ring of vertices at this latitude.
for(uint j=0; j<=segments_xz; j++,idx++)
{
float longitude=(j%segments_xz)*DX_PI_MUL2/segments_xz;
//--- normal
DXVector3 normal=DXVector3((float)sin(longitude)*dxz,dy,(float)cos(longitude)*dxz);
vertices[idx].normal =DXVector4(normal.x,normal.y,normal.z,0.0);
//--- position
DXVec3Scale(normal,normal,radius);
vertices[idx].position=DXVector4(normal.x,normal.y,normal.z,1.0);
//--- texture coords
tcoord.x = float(j)/segments_xz;
vertices[idx].tcoord =tcoord;
}
}
//--- fill the index buffer with triangles joining each pair of latitude rings.
uint stride=segments_xz+1;
uint idx=0;
for(uint i=0; i<segments_y; i++)
{
for(uint j=0; j<segments_xz; j++)
{
uint next_i=i+1;
uint next_j=(j+1)%stride;
indices[idx++]=next_i*stride + next_j;
indices[idx++]=next_i*stride + j;
indices[idx++]= i*stride + j;
indices[idx++]= i*stride + j;
indices[idx++]= i*stride + next_j;
indices[idx++]=next_i*stride + next_j;
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Torus |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeTorus(float outer_radius,float inner_radius,uint tessellation,TVertex &vertices[],uint &indices[])
{
if(tessellation<3)
tessellation=3;
//--- prepare arrays
uint count=(tessellation+1)*(tessellation+1);
if(ArrayResize(vertices,count)!=count)
return(false);
count=6*tessellation*tessellation;
if(ArrayResize(indices,count)!=count)
return(false);
//---
uint v=0,idx=0;
uint stride=tessellation+1;
//--- first we loop around the main ring of the torus.
for(uint i=0; i<=tessellation; i++)
{
DXVector2 tcoord=DXVector2(float(i)/tessellation,0.0f);
//--- create a transform matrix that will align geometry to slice perpendicularly though the current ring position.
DXMatrix rotation,transform;
DXMatrixRotationY(rotation,-1.0f*(i%tessellation)*DX_PI_MUL2/tessellation-DX_PI_DIV2);
DXMatrixTranslation(transform,outer_radius,0.0f,0.0f);
DXMatrixMultiply(transform,transform,rotation);
//--- now we loop along the other axis, around the side of the tube.
for(uint j=0; j<=tessellation; j++)
{
//--- calc normal and position
float angle=(j%tessellation)*DX_PI_MUL2/tessellation+DX_PI;
vertices[v].normal=DXVector4((float)cos(angle),(float)sin(angle),0.0f,0.0f);
vertices[v].position=DXVector4(vertices[v].normal.x*inner_radius,vertices[v].normal.y*inner_radius,0.0f,1.0f);
DXVec4Transform(vertices[v].normal, vertices[v].normal, transform);
DXVec4Transform(vertices[v].position,vertices[v].position,transform);
//--- calc texture coord
tcoord.y=1-float(j)/tessellation;
vertices[v].tcoord=tcoord;
v++;
//--- create indices for two triangles.
if(i<tessellation && j<tessellation)
{
uint next_i=i+1;
uint next_j=j+1;
indices[idx++]=next_i*stride + next_j;
indices[idx++]=next_i*stride + j;
indices[idx++]= i*stride + j;
indices[idx++]= i*stride + j;
indices[idx++]= i*stride + next_j;
indices[idx++]=next_i*stride + next_j;
}
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Cylinder |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeCylinder(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[])
{
return(DXComputeTruncatedCone(radius,radius,height,tessellation,vertices,indices));
}
//+------------------------------------------------------------------+
//| Truncated Cone |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeTruncatedCone(float radius_top,float radius_bottom,float height,uint tessellation,TVertex &vertices[],uint &indices[])
{
if(tessellation<3)
tessellation=3;
//--- prepare arrays
uint count=2*(tessellation+1)+2*tessellation;
if(ArrayResize(vertices,count)!=count)
return(false);
count=6*tessellation+6*(tessellation-1);
if(ArrayResize(indices,count)!=count)
return(false);
//--- prepare normal
DXVector2 normal=DXVector2(height,radius_bottom-radius_top);
DXVec2Normalize(normal,normal);
float dy=height/2.0f;
uint v=0,idx=0;
uint stride=2;
//--- create top and bottom rings of vertices
for(uint i=0; i<=tessellation; i++)
{
float u=1.0f-(float)i/tessellation;
float angle=(i*DX_PI_MUL2/tessellation);
float dx=(float)sin(angle);
float dz=(float)cos(angle);
//---
vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f);
vertices[v].position=DXVector4(dx*radius_bottom,-dy,dz*radius_bottom,1.0f);
vertices[v].tcoord =DXVector2(u,1.0f);
v++;
vertices[v].normal =vertices[v-1].normal;
vertices[v].position=DXVector4(dx*radius_top,dy,dz*radius_top,1.0f);
vertices[v].tcoord =DXVector2(u,0.0f);
v++;
//--- creater side surface
if(i<tessellation)
{
uint next_i=i+1;
indices[idx++]=next_i*stride + 1;
indices[idx++]= i*stride + 0;
indices[idx++]=next_i*stride + 0;
indices[idx++]= i*stride + 0;
indices[idx++]=next_i*stride + 1;
indices[idx++]= i*stride + 1;
}
}
//--- first cap vertex
uint cap_first=v;
//--- create caps
for(uint i=0; i<tessellation; i++)
{
float angle=(i*DX_PI_MUL2/tessellation);
float dx=(float)sin(angle);
float dz=(float)cos(angle);
vertices[v].normal =DXVector4(0.0f,-1.0f,0.0f,0.0f);
vertices[v].position=DXVector4(dx*radius_bottom,-dy,dz*radius_bottom,1.0f);
vertices[v].tcoord =DXVector2(0.5f+0.5f*dx,0.5f+0.5f*dz);
v++;
vertices[v].normal =DXVector4(0.0f,1.0f,0.0f,0.0f);
vertices[v].position=DXVector4(dx*radius_top,dy,dz*radius_top,1.0f);
vertices[v].tcoord =DXVector2(0.5f+0.5f*dx,0.5f-0.5f*dz);
v++;
//--- creater caps surface
if(i>0 && i<tessellation-1)
{
uint next_i=i+1;
indices[idx++]=cap_first;
indices[idx++]=cap_first+next_i*stride;
indices[idx++]=cap_first+ i*stride;
indices[idx++]=cap_first+1;
indices[idx++]=cap_first+ i*stride+1;
indices[idx++]=cap_first+next_i*stride+1;
}
}
return(true);
}
//+------------------------------------------------------------------+
//| Cone |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeCone(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[])
{
if(tessellation<3)
tessellation=3;
//--- prepare arrays
uint count=2*(tessellation+1)+tessellation;
if(ArrayResize(vertices,count)!=count)
return(false);
count=3*tessellation+3*(tessellation-1);
if(ArrayResize(indices,count)!=count)
return(false);
//--- prepare normal
DXVector2 normal=DXVector2(height,radius);
DXVec2Normalize(normal,normal);
float dy=height/2.0f;
uint v=0,idx=0;
uint stride=2;
//--- create top and bottom rings of vertices
for(uint i=0; i<=tessellation; i++)
{
float u=1.0f-(float)i/tessellation;
float angle=(i*DX_PI_MUL2/tessellation);
float dx=(float)sin(angle);
float dz=(float)cos(angle);
//---
vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f);
vertices[v].position=DXVector4(dx*radius,-dy,dz*radius,1.0f);
vertices[v].tcoord =DXVector2(u,1.0f);
v++;
vertices[v].normal =vertices[v-1].normal;
vertices[v].position=DXVector4(0.0f,dy,0.0f,1.0f);
vertices[v].tcoord =DXVector2(u,0.0f);
v++;
//--- creater side surface
if(i<tessellation)
{
uint next_i=i+1;
indices[idx++]=next_i*stride + 1;
indices[idx++]= i*stride + 0;
indices[idx++]=next_i*stride + 0;
}
}
//--- first cap vertex
uint cap_first=v;
//--- create caps
for(uint i=0; i<tessellation; i++)
{
float angle=(i*DX_PI_MUL2/tessellation);
float dx=(float)sin(angle);
float dz=(float)cos(angle);
vertices[v].normal =DXVector4(0.0f,-1.0f,0.0f,0.0f);
vertices[v].position=DXVector4(dx*radius,-dy,dz*radius,1.0f);
vertices[v].tcoord =DXVector2(0.5f+0.5f*dx,0.5f+0.5f*dz);
v++;
//--- creater caps surface
if(i>0 && i<tessellation-1)
{
indices[idx++]=cap_first;
indices[idx++]=cap_first+i+1;
indices[idx++]=cap_first+i;
}
}
return(true);
}
//+------------------------------------------------------------------+
//| Surface |
//| TVertex must have |
//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members|
//+------------------------------------------------------------------+
template <typename TVertex>
bool DXComputeSurface(double &data[],uint data_width,uint data_height,double data_range,
const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,
bool two_sided,bool use_normals,
TVertex &vertices[],uint &indices[])
{
//---
if(data_width<2 || data_height<2)
return(false);
//--- prepare arrays for vertices and triangles
uint count=data_width*data_height*(two_sided?2:1);
if(!ArrayResize(vertices,count))
return(false);
count=6*(data_width-1)*(data_height-1)*(two_sided?2:1);
if(!ArrayResize(indices,count))
return(false);
//--- find min and max value
float min_value=+FLT_MAX;
float max_value=-FLT_MAX;
for(uint j=0; j<data_height; j++)
for(uint i=0; i<data_width; i++)
{
float value=(float)data[j*data_width+i];
if(min_value>value)
min_value=value;
if(max_value<value)
max_value=value;
}
//--- check and fix data range
float avg_value=0.5f*(max_value+min_value);
float range=(float)data_range;
if(range<=0.0f)
range=max_value-min_value;
if(range<FLT_EPSILON)
range=FLT_EPSILON;
//---
min_value=avg_value-0.5f*range;
max_value=avg_value+0.5f*range;
//---
DXVector3 range_3d;
DXVec3Subtract(range_3d,to,from);
float step_x=range_3d.x/(data_width-1);
float step_y=range_3d.y/range;
float step_z=range_3d.z/(data_height-1);
//--- calculate vertices positions and colors
for(uint j=0; j<data_height; j++)
{
for(uint i=0; i<data_width; i++)
{
uint idx=j*data_width+i;
//--- calc value
float value=(float)data[idx]-min_value;
//--- calc position
vertices[idx].position.x = from.x+i *step_x;
vertices[idx].position.y = from.y+value*step_y;
vertices[idx].position.z = from.z+j *step_z;
vertices[idx].position.w = 1.0f;
//--- set texture coordinates
vertices[idx].tcoord=DXVector2(i*step_x/texture_size.x,j*step_z/texture_size.y);
}
}
//--- calculate normals
if(use_normals)
{
for(uint j=0; j<data_height; j++)
{
for(uint i=0; i<data_width; i++)
{
DXVector4 v1,v2,normal;
//--- v1
if(i<=0)
DXVec4Subtract(v1,vertices[j*data_width+i].position,vertices[j*data_width+i+1].position);
else
if(i>=data_width-1)
DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i].position);
else
DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i+1].position);
//--- v2
if(j<=0)
DXVec4Subtract(v2,vertices[j*data_width+i].position,vertices[(j+1)*data_width+i].position);
else
if(j>=data_height-1)
DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[j*data_width+i].position);
else
DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[(j+1)*data_width+i].position);
//--- normal
DXVec4Cross(normal,v2,v1,DXVector4(0.0f,0.0f,0.0f,1.0f));
float inv_len=(float)(1.0/sqrt(normal.x*normal.x+normal.y*normal.y+normal.z*normal.z));
vertices[j*data_width+i].normal.x=normal.x*inv_len;
vertices[j*data_width+i].normal.y=normal.y*inv_len;
vertices[j*data_width+i].normal.z=normal.z*inv_len;
vertices[j*data_width+i].normal.w=0.0f;
}
}
}
else
{
DXVector4 n=DXVector4(0.0f,0.0f,0.0f,0.0f);
for(int i=0; i<ArraySize(vertices); i++)
vertices[i].normal=n;
}
//--- calculate triangles for every rectangle
int n=0;
for(uint i=0; i<data_width-1; i++)
{
for(uint j=0; j<data_height-1; j++)
{
//--- left triangle
indices[n++]=j*data_width+(i+1);
indices[n++]=j*data_width+i;
indices[n++]=(j+1)*data_width+i;
//--- right triangle
indices[n++]=(j+1)*data_width+(i+1);
indices[n++]=j*data_width+(i+1);
indices[n++]=(j+1)*data_width+(i);
}
}
//--- generate back side
if(two_sided)
{
uint offset=data_height*data_width;
for(uint j=0; j<data_height; j++)
{
for(uint i=0; i<data_width; i++)
{
uint idx=offset+j*data_width+i;
//--- copy vertices in backward direction
vertices[idx]=vertices[j*data_width+data_width-i-1];
//--- inverse normals
DXVec4Scale(vertices[idx].normal,vertices[idx].normal,-1.0f);
}
}
//--- calculate triangles for opposit side
for(uint i=0; i<data_width-1; i++)
{
for(uint j=0; j<data_height-1; j++)
{
//--- left triangle
indices[n++]=offset+j*data_width+(i+1);
indices[n++]=offset+j*data_width+i;
indices[n++]=offset+(j+1)*data_width+i;
//--- right triangle
indices[n++]=offset+(j+1)*data_width+(i+1);
indices[n++]=offset+j*data_width+(i+1);
indices[n++]=offset+(j+1)*data_width+(i);
}
}
}
//---
return(true);
}
//+---------------------------------------------------------------------+
//| Computes Matlab jet color scheme colors on [0;1] range |
//| dark blue > blue > light blue > green > yellow > red > dark red |
//+---------------------------------------------------------------------+
void DXComputeColorJet(const float value,DXColor &cout)
{
float v=value*1.1f-0.05f;
cout.r = fmin(fmax(v<0.75f ? 4*v-1.5f : 4.5f-4*v,0.0f),1.0f);
cout.g = fmin(fmax(v<0.5f ? 4*v-0.5f : 3.5f-4*v,0.0f),1.0f);
cout.b = fmin(fmax(v<0.25f ? 4*v+0.5f : 2.5f-4*v,0.0f),1.0f);
cout.a = 1.0;
}
//+---------------------------------------------------------------------+
//| Computes hot to cold color scheme colors on [0;1] range |
//| blue > light blue > green > yellow > red |
//+---------------------------------------------------------------------+
void DXComputeColorColdToHot(const float value,DXColor &cout)
{
float v=2*value-1.0f;
cout.r = fmin(fmax(2*v,0.0f),1.0f);
cout.g = fmin(fmax(2.0f-2*fabs(v),0.0f),1.0f);
cout.b = fmin(fmax(-2*v,0.0f),1.0f);
cout.a = 1.0;
}
//+---------------------------------------------------------------------+
//| Computes red to green color scheme colors on [0;1] range |
//| red > yellow > dark green |
//+---------------------------------------------------------------------+
void DXComputeColorRedToGreen(const float value,DXColor &cout)
{
if(value<=0.5)
{
cout.r=1.0f;
cout.g=DXScalarLerp(0.01f,0.95f,fmin(fmax(2*value,0.0f),1.0f));
}
else
{
cout.r=DXScalarLerp(0.1f,1.0f, fmin(fmax(2.0f-2*value,0.0f),1.0f));
cout.g=DXScalarLerp(0.6f,0.95f,fmin(fmax(2.0f-2*value,0.0f),1.0f));
}
cout.b=0.0f;
cout.a=1.0f;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,71 @@
//+------------------------------------------------------------------+
//| Default Pixel Shader |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Inputs for whole scene |
//+------------------------------------------------------------------+
cbuffer InputScene : register(b0)
{
matrix view;
matrix projection;
float4 light_direction;
float4 light_color;
float4 ambient_color;
};
//+------------------------------------------------------------------+
//| Inputs for single object |
//+------------------------------------------------------------------+
cbuffer InputObject : register(b1)
{
matrix transform;
float4 diffuse_color;
float4 emission_color;
float4 specular_color;
float specular_power;
float dummy[3];
};
//+------------------------------------------------------------------+
//| Input texture |
//+------------------------------------------------------------------+
Texture2D<float4> diffuse_tex : register(t0);
//+------------------------------------------------------------------+
//| Texture sampler |
//+------------------------------------------------------------------+
SamplerState diffuse_samp
{
Filter =MIN_MAG_MIP_LINEAR;
AddressU=Wrap;
AddressV=Wrap;
};
//+------------------------------------------------------------------+
//| Pixel shader input type |
//+------------------------------------------------------------------+
struct PSInput
{
float4 position : SV_POSITION;
float4 camera : CAMERA;
float4 normal : NORMAL;
float2 tcoord : TEXCOORD;
float4 color : COLOR;
};
//+------------------------------------------------------------------+
//| Pixel shader entry point |
//+------------------------------------------------------------------+
float4 PSMain(PSInput input) : SV_TARGET
{
float3 diffuse =saturate(-dot(light_direction.xyz,input.normal.xyz))*light_color.rgb*light_color.a;
float3 ambient =ambient_color.rgb *ambient_color.a;
float3 light =(diffuse+ambient)*diffuse_color.rgb*diffuse_color.a+emission_color.rgb*emission_color.a;
float4 specular=float4(light_color.rgb*specular_color.rgb,pow(saturate(dot(reflect(normalize(light_direction.xyz),input.normal.xyz),normalize(input.camera.xyz))),specular_power)*light_color.a*specular_color.a);
float4 clr=input.color;
//--- use texture if it exist
uint width,height;
diffuse_tex.GetDimensions(width,height);
if(width*height>0)
clr*=diffuse_tex.Sample(diffuse_samp,frac(input.tcoord));
//--- combine light with colors
return(lerp(float4(light*clr.rgb,clr.a),float4(specular.rgb,1.0),specular.a));
}
//+------------------------------------------------------------------+
@@ -0,0 +1,72 @@
//+------------------------------------------------------------------+
//| Default Vertex Shader |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Inputs for whole scene |
//+------------------------------------------------------------------+
cbuffer InputScene : register(b0)
{
matrix view;
matrix projection;
float4 light_direction;
float4 light_color;
float4 ambient_color;
};
//+------------------------------------------------------------------+
//| Inputs for single object |
//+------------------------------------------------------------------+
cbuffer InputObject : register(b1)
{
matrix transform;
float4 diffuse_color;
float4 emission_color;
float4 specular_color;
float specular_power;
float dummy[3];
};
//+------------------------------------------------------------------+
//| Vertex shader input type |
//+------------------------------------------------------------------+
struct VSInput
{
float4 position : POSITION;
float4 normal : NORMAL;
float2 tcoord : TEXCOORD;
float4 color : COLOR;
};
//+------------------------------------------------------------------+
//| Pixel shader input type |
//+------------------------------------------------------------------+
struct PSInput
{
float4 position : SV_POSITION;
float4 camera : CAMERA;
float4 normal : NORMAL;
float2 tcoord : TEXCOORD;
float4 color : COLOR;
};
//+------------------------------------------------------------------+
//| Vertex shader entry point |
//+------------------------------------------------------------------+
PSInput VSMain(VSInput input)
{
PSInput output;
//--- posiiton and camera direction
output.position=mul(input .position,transform);
output.position=mul(output.position,view);
output.camera =-output.position;
output.position=mul(output.position,projection);
//--- transform normals
output.normal = mul(input.normal, transform);
output.normal = mul(output.normal, view);
output.normal = normalize(output.normal);
//--- color and texture coordinates
output.tcoord =input.tcoord;
output.color =input.color;
//---
return(output);
}
//+------------------------------------------------------------------+
+751
View File
@@ -0,0 +1,751 @@
//+------------------------------------------------------------------+
//| FlameCanvas.mqh |
//| Copyright 2009-2017, MetaQuotes Software Corp. |
//| http://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());
}
//+------------------------------------------------------------------+