Add files via upload
This commit is contained in:
+4861
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| HistogramChart.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://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);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,383 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| LineChart.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "ChartCanvas.mqh"
|
||||
#include <Arrays\ArrayObj.mqh>
|
||||
//+------------------------------------------------------------------+
|
||||
//| Class CLineChart |
|
||||
//| Usage: generates line chart |
|
||||
//+------------------------------------------------------------------+
|
||||
class CLineChart : public CChartCanvas
|
||||
{
|
||||
private:
|
||||
//--- data
|
||||
CArrayObj *m_values;
|
||||
//--- adjusted parameters
|
||||
bool m_filled;
|
||||
|
||||
public:
|
||||
CLineChart(void);
|
||||
~CLineChart(void);
|
||||
//--- create
|
||||
virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_ARGB_NORMALIZE);
|
||||
//--- adjusted parameters
|
||||
void Filled(const bool flag=true) { m_filled=flag; }
|
||||
//--- set up
|
||||
bool SeriesAdd(const double &value[],const string descr="",const uint clr=0);
|
||||
bool SeriesInsert(const uint pos,const double &value[],const string descr="",const uint clr=0);
|
||||
bool SeriesUpdate(const uint pos,const double &value[],const string descr=NULL,const uint clr=0);
|
||||
bool SeriesDelete(const uint pos);
|
||||
bool ValueUpdate(const uint series,const uint pos,double value);
|
||||
|
||||
protected:
|
||||
virtual void DrawChart(void);
|
||||
virtual void DrawData(const uint index=0);
|
||||
|
||||
private:
|
||||
double CalcArea(const uint index);
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CLineChart::CLineChart(void) : m_filled(false)
|
||||
{
|
||||
ShowFlags(FLAG_SHOW_LEGEND|FLAGS_SHOW_SCALES|FLAG_SHOW_GRID);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CLineChart::~CLineChart(void)
|
||||
{
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create dynamic resource |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
|
||||
{
|
||||
//--- create object to store data
|
||||
if((m_values=new CArrayObj)==NULL)
|
||||
return(false);
|
||||
//--- pass responsibility for its destruction to the parent class
|
||||
m_data=m_values;
|
||||
//--- call method of parent class
|
||||
if(!CChartCanvas::Create(name,width,height,clrfmt))
|
||||
return(false);
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Adds data series |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::SeriesAdd(const double &value[],const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if(m_data_total==m_max_data)
|
||||
return(false);
|
||||
//--- add
|
||||
CArrayDouble *arr=new CArrayDouble;
|
||||
if(!m_values.Add(arr))
|
||||
return(false);
|
||||
if(!arr.AssignArray(value))
|
||||
return(false);
|
||||
if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr))
|
||||
return(false);
|
||||
if(!m_descriptors.Add(descr))
|
||||
return(false);
|
||||
m_data_total++;
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Inserts data series |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::SeriesInsert(const uint pos,const double &value[],const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if(m_data_total==m_max_data)
|
||||
return(false);
|
||||
if(pos>=m_data_total)
|
||||
return(false);
|
||||
//--- insert
|
||||
CArrayDouble *arr=new CArrayDouble;
|
||||
if(!m_values.Insert(arr,pos))
|
||||
return(false);
|
||||
if(!arr.AssignArray(value))
|
||||
return(false);
|
||||
if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos))
|
||||
return(false);
|
||||
if(!m_descriptors.Insert(descr,pos))
|
||||
return(false);
|
||||
m_data_total++;
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates data series |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::SeriesUpdate(const uint pos,const double &value[],const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if(pos>=m_data_total)
|
||||
return(false);
|
||||
CArrayDouble *data=m_values.At(pos);
|
||||
if(data==NULL)
|
||||
return(false);
|
||||
//--- update
|
||||
if(!data.AssignArray(value))
|
||||
return(false);
|
||||
if(clr!=0 && !m_colors.Update(pos,clr))
|
||||
return(false);
|
||||
if(descr!=NULL && !m_descriptors.Update(pos,descr))
|
||||
return(false);
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deletes data series |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::SeriesDelete(const uint pos)
|
||||
{
|
||||
//--- check
|
||||
if(pos>=m_data_total && m_data_total!=0)
|
||||
return(false);
|
||||
//--- delete
|
||||
if(!m_values.Delete(pos))
|
||||
return(false);
|
||||
m_data_total--;
|
||||
if(!m_colors.Delete(pos))
|
||||
return(false);
|
||||
if(!m_descriptors.Delete(pos))
|
||||
return(false);
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates element in data series |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLineChart::ValueUpdate(const uint series,const uint pos,double value)
|
||||
{
|
||||
CArrayDouble *data=m_values.At(series);
|
||||
//--- check
|
||||
if(data==NULL)
|
||||
return(false);
|
||||
//--- update
|
||||
if(!data.Update(pos,value))
|
||||
return(false);
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Redraws data |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLineChart::DrawChart(void)
|
||||
{
|
||||
if(m_filled)
|
||||
{
|
||||
//--- calculate areas of filling
|
||||
double s[];
|
||||
ArrayResize(s,m_data_total);
|
||||
ArrayInitialize(s,0);
|
||||
for(uint i=0;i<m_data_total;i++)
|
||||
{
|
||||
CArrayDouble *data=m_values.At(i);
|
||||
if(data==NULL)
|
||||
continue;
|
||||
int total=data.Total();
|
||||
if(total<=1)
|
||||
continue;
|
||||
s[i]=CalcArea(i);
|
||||
}
|
||||
int index=ArrayMaximum(s);
|
||||
while(index!=-1 && s[index]!=0.0)
|
||||
{
|
||||
//--- draw in area descending order
|
||||
DrawData(index);
|
||||
s[index]=0.0;
|
||||
index=ArrayMaximum(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
for(uint i=0;i<m_data_total;i++)
|
||||
DrawData(i);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws lines |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLineChart::DrawData(const uint index)
|
||||
{
|
||||
double value=0.0;
|
||||
//--- check
|
||||
CArrayDouble *data=m_values.At(index);
|
||||
if(data==NULL)
|
||||
return;
|
||||
int total=data.Total();
|
||||
if(total<=1)
|
||||
return;
|
||||
//--- calculate
|
||||
int dx=m_data_area.Width()/(total-1);
|
||||
int x=m_data_area.left+1;
|
||||
int y1=0;
|
||||
int y2=(int)(m_y_0-data[0]*m_scale_y);
|
||||
//--- draw
|
||||
for(int i=1;i<total;i++,x+=dx)
|
||||
{
|
||||
y1=y2;
|
||||
double val=data[i];
|
||||
if(val==EMPTY_VALUE)
|
||||
continue;
|
||||
if(m_accumulative)
|
||||
value+=val;
|
||||
else
|
||||
value=val;
|
||||
y2=(int)(m_y_0-value*m_scale_y);
|
||||
if(m_filled)
|
||||
{
|
||||
if((y1>m_y_0 && y2<m_y_0) || (y1<m_y_0 && y2>m_y_0))
|
||||
{
|
||||
//--- draw two triangles
|
||||
int x3;
|
||||
if(y1>y2)
|
||||
{
|
||||
x3=x+dx*(y1-m_y_0)/(y1-y2);
|
||||
FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]);
|
||||
FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
x3=x+dx*(m_y_0-y1)/(y2-y1);
|
||||
FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]);
|
||||
FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(y1<m_y_0 || y2<m_y_0)
|
||||
{
|
||||
if(y1>y2)
|
||||
FillTriangle(x,y1,x+dx,y2,x+dx,y1,(uint)m_colors[index]);
|
||||
if(y1<y2)
|
||||
{
|
||||
FillTriangle(x,y1,x+dx,y2,x,y2,(uint)m_colors[index]);
|
||||
y1=y2;
|
||||
}
|
||||
}
|
||||
if(y1>m_y_0 || y2>m_y_0)
|
||||
{
|
||||
if(y1<y2)
|
||||
FillTriangle(x,y1,x+dx,y2,x+dx,y1,(uint)m_colors[index]);
|
||||
if(y1>y2)
|
||||
{
|
||||
FillTriangle(x,y1,x+dx,y2,x,y2,(uint)m_colors[index]);
|
||||
y1=y2;
|
||||
}
|
||||
}
|
||||
FillRectangle(x,m_y_0,x+dx,y1,(uint)m_colors[index]);
|
||||
}
|
||||
else
|
||||
LineAA(x,y1,x+dx,y2,(uint)m_colors[index],STYLE_SOLID);
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Area of filling |
|
||||
//+------------------------------------------------------------------+
|
||||
double CLineChart::CalcArea(const uint index)
|
||||
{
|
||||
double area =0;
|
||||
double value=0;
|
||||
int dx =100;
|
||||
//---
|
||||
CArrayDouble *data=m_values.At(index);
|
||||
if(data==NULL)
|
||||
return(0);
|
||||
int total=data.Total();
|
||||
if(total<=1)
|
||||
return(0);
|
||||
int y1=0;
|
||||
int y2=(int)(m_y_0-data[0]*m_scale_y);
|
||||
for(int i=0;i<total;i++)
|
||||
{
|
||||
y1=y2;
|
||||
double val=data[i];
|
||||
if(val==EMPTY_VALUE)
|
||||
continue;
|
||||
if(m_accumulative)
|
||||
value+=val;
|
||||
else
|
||||
value=val;
|
||||
y2=(int)(m_y_0-value*m_scale_y);
|
||||
if((y1>m_y_0 && y2<m_y_0) || (y1<m_y_0 && y2>m_y_0))
|
||||
{
|
||||
//--- line of values crosses the Y axis
|
||||
int x;
|
||||
if(y1>y2)
|
||||
{
|
||||
//--- from the bottom up
|
||||
x=dx*(y1-m_y_0)/(y1-y2);
|
||||
//--- add area of lower triangle
|
||||
area+=x*(y1-m_y_0)/2;
|
||||
//--- add area of upper triangle
|
||||
area+=(dx-x)*(m_y_0-y2)/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
//--- from top down
|
||||
x=dx*(m_y_0-y1)/(y2-y1);
|
||||
//--- add area of upper triangle
|
||||
area+=x*(m_y_0-y1)/2;
|
||||
//--- add area of lower triangle
|
||||
area+=(dx-x)*(y2-m_y_0)/2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(y1<m_y_0 || y2<m_y_0)
|
||||
{
|
||||
//--- both values are greater than zero
|
||||
if(y1>y2)
|
||||
{
|
||||
//--- add area of triangle
|
||||
area+=dx*(y1-y2)/2;
|
||||
//--- add area of rectangle
|
||||
area+=dx*(m_y_0-y2);
|
||||
}
|
||||
if(y1<y2)
|
||||
{
|
||||
//--- add area of triangle
|
||||
area+=dx*(y2-y1)/2;
|
||||
//--- add area of rectangle
|
||||
area+=dx*(m_y_0-y1);
|
||||
}
|
||||
}
|
||||
if(y1>m_y_0 || y2>m_y_0)
|
||||
{
|
||||
//--- both values are less than zero
|
||||
if(y1<y2)
|
||||
{
|
||||
//--- add area of triangle
|
||||
area+=dx*(y2-y1)/2;
|
||||
//--- add area of rectangle
|
||||
area+=dx*(y1-m_y_0);
|
||||
}
|
||||
if(y1>y2)
|
||||
{
|
||||
//--- add area of triangle
|
||||
area+=dx*(y1-y2)/2;
|
||||
//--- add area of rectangle
|
||||
area+=dx*(y2-m_y_0);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---
|
||||
return(area);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,414 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| PieChart.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "ChartCanvas.mqh"
|
||||
//+------------------------------------------------------------------+
|
||||
//| Class CPieChart |
|
||||
//| Usage: generates pie chart |
|
||||
//+------------------------------------------------------------------+
|
||||
class CPieChart : public CChartCanvas
|
||||
{
|
||||
private:
|
||||
//--- data
|
||||
CArrayDouble *m_values;
|
||||
//--- for draw
|
||||
int m_x0;
|
||||
int m_y0;
|
||||
int m_r;
|
||||
|
||||
public:
|
||||
CPieChart(void);
|
||||
~CPieChart(void);
|
||||
//--- create
|
||||
virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
|
||||
//--- data
|
||||
bool SeriesSet(const double &value[],const string &text[],const uint &clr[]);
|
||||
bool ValueAdd(const double value,const string descr="",const uint clr=0);
|
||||
bool ValueInsert(const uint pos,const double value,const string descr="",const uint clr=0);
|
||||
bool ValueUpdate(const uint pos,const double value,const string descr=NULL,const uint clr=0);
|
||||
bool ValueDelete(const uint pos);
|
||||
|
||||
protected:
|
||||
virtual void DrawChart(void);
|
||||
void DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr);
|
||||
string LabelMake(const string text,const double value,const bool to_left);
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CPieChart::CPieChart(void)
|
||||
{
|
||||
uint flags=FLAG_SHOW_LEGEND|FLAG_SHOW_DESCRIPTORS|FLAG_SHOW_VALUE|FLAG_SHOW_PERCENT;
|
||||
AllowedShowFlags(flags);
|
||||
ShowFlags(flags);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CPieChart::~CPieChart(void)
|
||||
{
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create dynamic resource |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
|
||||
{
|
||||
//--- create object to store data
|
||||
if((m_values=new CArrayDouble)==NULL)
|
||||
return(false);
|
||||
//--- pass responsibility for its destruction to the parent class
|
||||
m_data=m_values;
|
||||
//--- call method of parent class
|
||||
if(!CChartCanvas::Create(name,width,height,clrfmt))
|
||||
return(false);
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sets displayed parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::SeriesSet(const double &value[],const string &text[],const uint &clr[])
|
||||
{
|
||||
//--- !!! user is responsible for correct filling of arrays !!!
|
||||
//--- check
|
||||
if(m_values==NULL)
|
||||
return(false);
|
||||
//--- set
|
||||
if(!m_values.AssignArray(value))
|
||||
return(false);
|
||||
if(!m_descriptors.AssignArray(text))
|
||||
return(false);
|
||||
if(!m_colors.AssignArray(clr))
|
||||
return(false);
|
||||
m_data_total=m_values.Total();
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Adds displayed parameter (to the end) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::ValueAdd(const double value,const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if((value<=0))
|
||||
return(false);
|
||||
//--- add
|
||||
if(!m_values.Add(value))
|
||||
return(false);
|
||||
if(!m_descriptors.Add(descr))
|
||||
return(false);
|
||||
if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr))
|
||||
return(false);
|
||||
m_data_total++;
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Inserts displayed parameter (to specified position) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::ValueInsert(const uint pos,const double value,const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if((value<=0))
|
||||
return(false);
|
||||
//--- insert
|
||||
if(!m_values.Insert(value,pos))
|
||||
return(false);
|
||||
if(!m_descriptors.Insert(descr,pos))
|
||||
return(false);
|
||||
if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos))
|
||||
return(false);
|
||||
m_data_total++;
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates displayed parameter (in specified position) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::ValueUpdate(const uint pos,const double value,const string descr,const uint clr)
|
||||
{
|
||||
//--- check
|
||||
if((value<=0))
|
||||
return(false);
|
||||
//--- update
|
||||
if(!m_values.Update(pos,value))
|
||||
return(false);
|
||||
if(descr!=NULL && !m_descriptors.Update(pos,descr))
|
||||
return(false);
|
||||
if(clr!=0 && !m_colors.Update(pos,clr))
|
||||
return(false);
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deletes displayed parameter (from specified position) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CPieChart::ValueDelete(const uint pos)
|
||||
{
|
||||
//--- delete
|
||||
if(!m_values.Delete(pos))
|
||||
return(false);
|
||||
m_data_total--;
|
||||
if(!m_descriptors.Delete(pos))
|
||||
return(false);
|
||||
if(!m_colors.Delete(pos))
|
||||
return(false);
|
||||
//--- redraw
|
||||
Redraw();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws |
|
||||
//+------------------------------------------------------------------+
|
||||
void CPieChart::DrawChart(void)
|
||||
{
|
||||
//--- check
|
||||
if(m_data_total==0)
|
||||
return;
|
||||
//--- variables
|
||||
string text="";
|
||||
double angle=M_PI*(m_data_offset%360)/180;
|
||||
int width,height;
|
||||
int dw=0;
|
||||
int dh=0;
|
||||
int index;
|
||||
CPoint p0[];
|
||||
CPoint p1[];
|
||||
//--- calculate geometry
|
||||
width =(m_data_area.Width()<<3)/10;
|
||||
height=(m_data_area.Height()<<3)/10;
|
||||
if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS)
|
||||
{
|
||||
if(IS_SHOW_VALUE)
|
||||
dw=(int)m_max_value_width;
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
dw=TextWidth("100.00%");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_DESCRIPTORS)
|
||||
{
|
||||
if(IS_SHOW_VALUE)
|
||||
dw=(int)m_max_value_width+TextWidth(" ()");
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
dw=TextWidth(" (100.00%)");
|
||||
}
|
||||
dw+=(int)m_max_descr_width;
|
||||
}
|
||||
}
|
||||
//--- pie chart will always be round
|
||||
width -=2*dw+10;
|
||||
height-=20;
|
||||
m_x0=m_data_area.left+(m_data_area.Width()>>1);
|
||||
m_y0=m_data_area.top+(m_data_area.Height()>>1);
|
||||
m_r =(((width>height) ? height : width)>>1);
|
||||
//--- draw pie chart
|
||||
if(ArrayResize(p0,m_data_total+1)==-1)
|
||||
return;
|
||||
if(m_data_total==1)
|
||||
{
|
||||
FillCircle(m_x0,m_y0,m_r,m_colors[0]);
|
||||
Circle(m_x0,m_y0,m_r,m_color_border);
|
||||
}
|
||||
else
|
||||
{
|
||||
Circle(m_x0,m_y0,m_r,m_color_border);
|
||||
for(uint i=0;i<m_index_size;i++)
|
||||
{
|
||||
index=m_index[i];
|
||||
double val=m_values[index];
|
||||
double d_a=2*M_PI*val/m_sum;
|
||||
DrawPie(angle,angle+d_a,i,p0,m_colors[index]);
|
||||
angle+=d_a;
|
||||
angle=MathMod(angle,2*M_PI);
|
||||
}
|
||||
if(m_data_total!=m_index_size)
|
||||
DrawPie(angle,angle+2*M_PI*m_others/m_sum,m_index_size,p0,COLOR2RGB(clrBlack));
|
||||
for(uint i=0;i<=m_index_size;i++)
|
||||
Line(m_x0,m_y0,p0[i].x,p0[i].y,m_color_border);
|
||||
Circle(m_x0,m_y0,m_r,m_color_border);
|
||||
}
|
||||
//--- draw descriptors
|
||||
angle=M_PI*(m_data_offset%360)/180;
|
||||
int r1=(int)round(1.1*m_r);
|
||||
if(ArrayResize(p1,m_data_total)==-1)
|
||||
return;
|
||||
//--- calculations
|
||||
for(uint i=0;i<m_index_size;i++)
|
||||
{
|
||||
index=m_index[i];
|
||||
angle+=M_PI*m_values[index]/m_sum;
|
||||
//--- lines
|
||||
p0[i].x=m_x0+(int)round(m_r*cos(angle));
|
||||
p0[i].y=m_y0-(int)round(m_r*sin(angle));
|
||||
p1[i].x=m_x0+(int)round(r1*cos(angle));
|
||||
p1[i].y=m_y0-(int)round(r1*sin(angle));
|
||||
angle+=M_PI*m_values[index]/m_sum;
|
||||
}
|
||||
if(m_data_total!=m_index_size)
|
||||
{
|
||||
index=(int)m_data_total-1;
|
||||
angle+=M_PI*m_others/m_sum;
|
||||
p0[index].x=m_x0+(int)round(m_r*cos(angle));
|
||||
p0[index].y=m_y0-(int)round(m_r*sin(angle));
|
||||
p1[index].x=m_x0+(int)round(r1*cos(angle));
|
||||
p1[index].y=m_y0-(int)round(r1*sin(angle));
|
||||
}
|
||||
int x,y;
|
||||
//--- draw descriptors to the left
|
||||
for(uint i=0;i<m_index_size;i++)
|
||||
if(p0[i].x<m_x0)
|
||||
{
|
||||
x=p1[i].x-10;
|
||||
y=p1[i].y;
|
||||
index=m_index[i];
|
||||
text=LabelMake(m_descriptors[index],m_values[index],true);
|
||||
if(text!="")
|
||||
{
|
||||
Line(p0[i].x,p0[i].y,p1[i].x,p1[i].y,m_color_border);
|
||||
Line(p1[i].x,p1[i].y,x,y,m_color_border);
|
||||
TextOut(x-5,y,text,m_color_text,TA_RIGHT|TA_VCENTER);
|
||||
}
|
||||
}
|
||||
//--- draw descriptors to the right
|
||||
for(uint i=0;i<m_index_size;i++)
|
||||
if(p0[i].x>=m_x0)
|
||||
{
|
||||
x=p1[i].x+10;
|
||||
y=p1[i].y;
|
||||
index=m_index[i];
|
||||
text=LabelMake(m_descriptors[index],m_values[index],false);
|
||||
if(text!="")
|
||||
{
|
||||
Line(p0[i].x,p0[i].y,p1[i].x,p1[i].y,m_color_border);
|
||||
Line(p1[i].x,p1[i].y,x,y,m_color_border);
|
||||
TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER);
|
||||
}
|
||||
}
|
||||
if(m_data_total!=m_index_size)
|
||||
{
|
||||
index=(int)m_data_total-1;
|
||||
if(p0[index].x>=m_x0)
|
||||
{
|
||||
x=p1[index].x+10;
|
||||
y=p1[index].y;
|
||||
text=LabelMake("Others",m_others,true);
|
||||
TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER);
|
||||
}
|
||||
else
|
||||
{
|
||||
x=p1[index].x-10;
|
||||
y=p1[index].y;
|
||||
text=LabelMake("Others",m_others,false);
|
||||
TextOut(x-5,y,text,m_color_text,TA_RIGHT|TA_VCENTER);
|
||||
}
|
||||
if(text!="")
|
||||
{
|
||||
Line(p0[index].x,p0[index].y,p1[index].x,p1[index].y,m_color_border);
|
||||
Line(p1[index].x,p1[index].y,x,y,m_color_border);
|
||||
}
|
||||
}
|
||||
ArrayFree(p1);
|
||||
ArrayFree(p0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw pie |
|
||||
//+------------------------------------------------------------------+
|
||||
void CPieChart::DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr)
|
||||
{
|
||||
//--- draw arc
|
||||
Arc(m_x0,m_y0,m_r,m_r,fi3,fi4,p[idx].x,p[idx].y,p[idx+1].x,p[idx+1].y,clr);
|
||||
//--- variables
|
||||
int x3=p[idx].x;
|
||||
int y3=p[idx].y;
|
||||
int x4=p[idx+1].x;
|
||||
int y4=p[idx+1].y;
|
||||
//--- draw radii
|
||||
if(idx==0)
|
||||
Line(m_x0,m_y0,x3,y3,clr);
|
||||
if(idx!=m_data_total-1)
|
||||
Line(m_x0,m_y0,x4,y4,clr);
|
||||
//--- fill
|
||||
double fi=(fi3+fi4)/2;
|
||||
int xf=m_x0+(int)(0.99*m_r*cos(fi));
|
||||
int yf=m_y0-(int)(0.99*m_r*sin(fi));
|
||||
Fill(xf,yf,clr);
|
||||
//--- for small pie
|
||||
if(fi4-fi3<=M_PI_4)
|
||||
Line(m_x0,m_y0,xf,yf,clr);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Make label for pie |
|
||||
//+------------------------------------------------------------------+
|
||||
string CPieChart::LabelMake(const string text,const double value,const bool to_left)
|
||||
{
|
||||
string label="";
|
||||
//---
|
||||
if(to_left)
|
||||
{
|
||||
if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS)
|
||||
{
|
||||
if(IS_SHOW_VALUE)
|
||||
label=DoubleToString(value,2);
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
label=DoubleToString(100*value/m_sum,2)+"%";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
label=text;
|
||||
if(IS_SHOW_VALUE)
|
||||
label+=" ("+DoubleToString(value,2)+")";
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
label+=" ("+DoubleToString(100*value/m_sum,2)+"%)";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS)
|
||||
{
|
||||
if(IS_SHOW_VALUE)
|
||||
label=DoubleToString(value,2);
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
label=DoubleToString(100*value/m_sum,2)+"%";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_VALUE)
|
||||
label="("+DoubleToString(value,2)+") ";
|
||||
else
|
||||
{
|
||||
if(IS_SHOW_PERCENT)
|
||||
label="("+DoubleToString(100*value/m_sum,2)+"%) ";
|
||||
}
|
||||
label+=text;
|
||||
}
|
||||
}
|
||||
//---
|
||||
return(label);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXBox.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,92 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXVertexBuffer.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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.
@@ -0,0 +1,373 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXDispatcher.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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
@@ -0,0 +1,375 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXMesh.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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.
@@ -0,0 +1,162 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXSurface.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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.
@@ -0,0 +1,942 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DXUtils.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
|
||||
#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 2000-2024, MetaQuotes Ltd. |
|
||||
//| 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 2000-2024, MetaQuotes Ltd. |
|
||||
//| 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);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,751 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| FlameCanvas.mqh |
|
||||
//| Copyright 2000-2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "Canvas.mqh"
|
||||
#include <Controls\Defines.mqh>
|
||||
//+------------------------------------------------------------------+
|
||||
//| Gradient descriptors |
|
||||
//+------------------------------------------------------------------+
|
||||
struct GRADIENT_COLOR
|
||||
{
|
||||
uint clr; // color in ARGB format
|
||||
uint pos; // position of color in percentage of gradient range
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
struct GRADIENT_SIZE
|
||||
{
|
||||
uint size; // width of gradient fill in percentage of base fill
|
||||
uint pos; // position of color in percentage of gradient length
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Class CFlameCanvas |
|
||||
//| Usage: generates flame |
|
||||
//+------------------------------------------------------------------+
|
||||
class CFlameCanvas : public CCanvas
|
||||
{
|
||||
private:
|
||||
//--- parameters
|
||||
uint m_bar_gap;
|
||||
uint m_bar_width;
|
||||
uint m_chart_scale;
|
||||
double m_chart_price_min;
|
||||
double m_chart_price_max;
|
||||
ENUM_TIMEFRAMES m_timeframe;
|
||||
string m_symbol;
|
||||
int m_future_bars;
|
||||
int m_back_bars;
|
||||
int m_rates_total;
|
||||
uint m_palette[256]; // flame palette
|
||||
uchar m_flame[]; // buffer for calculation of flame
|
||||
uint m_time_redraw;
|
||||
uint m_delay;
|
||||
// bool m_resize_flag;
|
||||
// int m_tick_cnt;
|
||||
//--- flame parameters
|
||||
datetime m_tb1;
|
||||
double m_pb1;
|
||||
datetime m_te1;
|
||||
double m_pe1;
|
||||
datetime m_tb2;
|
||||
double m_pb2;
|
||||
datetime m_te2;
|
||||
double m_pe2;
|
||||
//--- equation parameters for flame
|
||||
int m_cloud_axis[100];
|
||||
double m_a1;
|
||||
double m_b1;
|
||||
double m_a2;
|
||||
double m_b2;
|
||||
int m_xb1;
|
||||
int m_yb1;
|
||||
int m_xe1;
|
||||
int m_ye1;
|
||||
int m_xb2;
|
||||
int m_yb2;
|
||||
int m_xe2;
|
||||
int m_ye2;
|
||||
|
||||
public:
|
||||
CFlameCanvas(void);
|
||||
~CFlameCanvas(void);
|
||||
//--- create
|
||||
bool FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars=0);
|
||||
void RatesTotal(const int value);
|
||||
//--- setting
|
||||
void PaletteSet(uint clr=0xFF0000);
|
||||
//--- draw
|
||||
void FlameDraw(const double &prices[],const int width,const int lenght);
|
||||
void FlameSet(datetime xb1,double yb1,datetime xe1,double ye1,datetime xb2,double yb2,datetime xe2,double ye2);
|
||||
//--- event handler
|
||||
void ChartEventHandler(const int id,const long &lparam,const double &dparam,const string &sparam);
|
||||
|
||||
protected:
|
||||
bool Resize(void);
|
||||
void ChartScale(void);
|
||||
void FlameSet(void);
|
||||
void CloudDraw(const double &prices[],const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[],const uchar t_level=255,const bool custom_gradient=true);
|
||||
void FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[]);
|
||||
void GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[]);
|
||||
void GradientVerticalLine(int x,int y1,int y2,const GRADIENT_COLOR &gradient[]);
|
||||
void GradientVerticalLineMonochrome(int x,int y1,int y2,uint clr1,uint clr2);
|
||||
void FlameCreate(void);
|
||||
void FlameCalculate(void);
|
||||
void Delay(const uint value);
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CFlameCanvas::CFlameCanvas(void) : m_bar_gap(16),
|
||||
m_bar_width(8),
|
||||
m_chart_scale(1),
|
||||
m_chart_price_min(0.0),
|
||||
m_chart_price_max(0.0),
|
||||
m_timeframe(PERIOD_CURRENT),
|
||||
m_symbol(NULL),
|
||||
m_future_bars(0),
|
||||
m_back_bars(0),
|
||||
m_rates_total(0),
|
||||
m_time_redraw(0),
|
||||
m_delay(50),
|
||||
m_tb1(0),
|
||||
m_pb1(0),
|
||||
m_te1(0),
|
||||
m_pe1(0),
|
||||
m_tb2(0),
|
||||
m_pb2(0),
|
||||
m_te2(0),
|
||||
m_pe2(0)
|
||||
{
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CFlameCanvas::~CFlameCanvas(void)
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Creates dynamic resource with object |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CFlameCanvas::FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars)
|
||||
{
|
||||
//--- get chart parameters
|
||||
ChartScale();
|
||||
//--- create
|
||||
int width =(int)m_bar_gap*(future_bars+back_bars);
|
||||
int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
|
||||
if(!CreateBitmap(0,0,name,time-back_bars*PeriodSeconds(),m_chart_price_max,width,height,COLOR_FORMAT_ARGB_NORMALIZE))
|
||||
return(false);
|
||||
ArrayResize(m_flame,width*height);
|
||||
//--- save parameters
|
||||
m_future_bars=future_bars;
|
||||
m_back_bars =back_bars;
|
||||
//--- settings
|
||||
PaletteSet();
|
||||
m_timeframe =Period();
|
||||
m_symbol =Symbol();
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Resize |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CFlameCanvas::Resize(void)
|
||||
{
|
||||
int x,y;
|
||||
//--- get limits
|
||||
double min=ChartGetDouble(0,CHART_PRICE_MIN);
|
||||
double max=ChartGetDouble(0,CHART_PRICE_MAX);
|
||||
if(m_chart_price_max!=max)
|
||||
{
|
||||
//--- move object
|
||||
ObjectSetDouble(0,m_objname,OBJPROP_PRICE,0,max);
|
||||
}
|
||||
//--- check
|
||||
if(m_chart_price_min==min && m_chart_price_max==max)
|
||||
return(false);
|
||||
m_chart_price_min=min;
|
||||
m_chart_price_max=max;
|
||||
//--- grt size
|
||||
ChartTimePriceToXY(0,0,m_tb1,min,x,y);
|
||||
int width =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS)-x;
|
||||
int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
|
||||
//--- resize
|
||||
if(width<m_width)
|
||||
width=m_width;
|
||||
if(width<=0)
|
||||
return(false);
|
||||
CCanvas::Resize(width,height);
|
||||
//--- resize flame buffer
|
||||
ArrayResize(m_flame,width*height);
|
||||
ArrayInitialize(m_flame,0);
|
||||
ArrayInitialize(m_pixels,0);
|
||||
//--- restore parameters
|
||||
if(m_pb1!=0.0)
|
||||
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
||||
//--- succeed
|
||||
return(true);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::RatesTotal(const int value)
|
||||
{
|
||||
if(value==0)
|
||||
return;
|
||||
if(m_rates_total==0)
|
||||
m_rates_total=value;
|
||||
else
|
||||
{
|
||||
if(m_rates_total!=value)
|
||||
{
|
||||
//--- move object
|
||||
ObjectSetInteger(0,m_objname,OBJPROP_TIME,0,
|
||||
ObjectGetInteger(0,m_objname,OBJPROP_TIME)+(value-m_rates_total)*PeriodSeconds());
|
||||
m_rates_total=value;
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Adjusts to the chart scale |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::ChartScale(void)
|
||||
{
|
||||
m_chart_scale=(uint)ChartGetInteger(0,CHART_SCALE);
|
||||
//--- set params
|
||||
switch(m_chart_scale)
|
||||
{
|
||||
case 0:
|
||||
m_bar_gap =1;
|
||||
m_bar_width=1;
|
||||
break;
|
||||
case 1:
|
||||
m_bar_gap =2;
|
||||
m_bar_width=1;
|
||||
break;
|
||||
case 2:
|
||||
m_bar_gap =4;
|
||||
m_bar_width=2;
|
||||
break;
|
||||
case 3:
|
||||
m_bar_gap =8;
|
||||
m_bar_width=4;
|
||||
break;
|
||||
case 4:
|
||||
m_bar_gap =16;
|
||||
m_bar_width=10;
|
||||
break;
|
||||
case 5:
|
||||
m_bar_gap =32;
|
||||
m_bar_width=22;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sets palette |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::PaletteSet(uint clr)
|
||||
{
|
||||
//--- create palette
|
||||
double g=0,b=0,dg=1.45,db=0.63;
|
||||
//---
|
||||
for(uint a,i=0;i<256;i++)
|
||||
{
|
||||
//--- the first 32 values ??of flame are completely transparent
|
||||
a=uchar(i<32?0:i-32);
|
||||
//--- generate color for the i value of flame
|
||||
m_palette[i]=(a<<24)|(uint(255)<<16)|(uint(g+0.5)<<8)|uint(b+0.5);
|
||||
//--- increment the color components
|
||||
//--- the red color gets gradient due to transparency
|
||||
if(i>80) g+=dg;
|
||||
if(i>160) b+=db;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws the flame |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameDraw(const double &prices[],const int width,const int lenght)
|
||||
{
|
||||
static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}};
|
||||
static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}};
|
||||
//--- draw
|
||||
CloudDraw(prices,width,lenght,sword,flame);
|
||||
//--- copy flame buffer
|
||||
FlameCalculate();
|
||||
//--- start timer
|
||||
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[])
|
||||
{
|
||||
//--- check
|
||||
int total=ArraySize(m_cloud_axis);
|
||||
if(total<2)
|
||||
return;
|
||||
if(total>lenght)
|
||||
total=lenght;
|
||||
//--- draw
|
||||
int xb,xe; // coordinates of the segment
|
||||
int ybm,yem; // coordinates of the center line
|
||||
int yb1,ye1; // coordinates of the first line
|
||||
int yb2,ye2; // coordinates of the second line
|
||||
//--- for implementation of variable width
|
||||
int w_total=ArraySize(size);
|
||||
if(w_total<2)
|
||||
return;
|
||||
int w_i =0;
|
||||
int w_is=(int)size[w_i].pos*total/100;
|
||||
int w_ie=(int)size[w_i+1].pos*total/100;
|
||||
double w =size[w_i].size*width/100;
|
||||
double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
||||
//--- draw from left to right
|
||||
xb=0;
|
||||
ybm=m_cloud_axis[0];
|
||||
yb1=ybm-(int)(w/2);
|
||||
yb2=ybm+(int)(w/2);
|
||||
//--- draw
|
||||
for(int i=1;i<total;i++)
|
||||
{
|
||||
xe=(int)(i*m_bar_gap);
|
||||
if(m_cloud_axis[i]==DBL_MAX)
|
||||
continue;
|
||||
yem=m_cloud_axis[i];
|
||||
w+=dw;
|
||||
ye1=yem-(int)(w/2);
|
||||
ye2=yem+(int)(w/2);
|
||||
//--- draw the segment of 'cloud'
|
||||
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
||||
xb=xe;
|
||||
if(xb>=m_width)
|
||||
break;
|
||||
yb1=ye1;
|
||||
yb2=ye2;
|
||||
while(i>=w_ie-1 && i!=total-1)
|
||||
{
|
||||
w_i++;
|
||||
w_is=(int)size[w_i].pos*total/100;
|
||||
w_ie=(int)size[w_i+1].pos*total/100;
|
||||
w =size[w_i].size*width/100;
|
||||
if(w_ie==w_is)
|
||||
{
|
||||
//--- for "instant" resize
|
||||
dw=size[w_i+1].size*width/100-w;
|
||||
w+=dw;
|
||||
ye1=yem-(int)(w/2);
|
||||
ye2=yem+(int)(w/2);
|
||||
//--- draw the segment of 'cloud'
|
||||
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
||||
yb1=ye1;
|
||||
yb2=ye2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//--- copy flame buffer
|
||||
FlameCalculate();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sets parameters of the flame and starts to draw |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameSet(void)
|
||||
{
|
||||
m_a1=m_bar_gap*((m_ye1-m_yb1)/((double)m_xe1-m_xb1));
|
||||
m_a2=m_bar_gap*((m_ye2-m_yb2)/((double)m_xe2-m_xb2));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sets parameters of the flame and starts to draw |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameSet(datetime tb1,double pb1,
|
||||
datetime te1,double pe1,
|
||||
datetime tb2,double pb2,
|
||||
datetime te2,double pe2)
|
||||
{
|
||||
datetime obj_time =(datetime)ObjectGetInteger(0,m_objname,OBJPROP_TIME);
|
||||
double obj_price=ObjectGetDouble(0,m_objname,OBJPROP_PRICE);
|
||||
int dx,dy;
|
||||
//--- save parameters
|
||||
m_tb1=tb1;
|
||||
m_pb1=pb1;
|
||||
m_te1=te1;
|
||||
m_pe1=pe1;
|
||||
m_tb2=tb2;
|
||||
m_pb2=pb2;
|
||||
m_te2=te2;
|
||||
m_pe2=pe2;
|
||||
//--- resize
|
||||
Resize();
|
||||
//--- convert
|
||||
if(ChartTimePriceToXY(0,0,obj_time,obj_price,dx,dy))
|
||||
{
|
||||
dy=m_yb1;
|
||||
if(ChartTimePriceToXY(0,0,tb1,pb1,m_xb1,m_yb1))
|
||||
if(ChartTimePriceToXY(0,0,te1,pe1,m_xe1,m_ye1))
|
||||
if(ChartTimePriceToXY(0,0,tb2,pb2,m_xb2,m_yb2))
|
||||
if(ChartTimePriceToXY(0,0,te2,pe2,m_xe2,m_ye2))
|
||||
{
|
||||
//--- convert to canvas coordinates
|
||||
m_xb1-=dx;
|
||||
m_xe1-=dx;
|
||||
m_xb2-=dx;
|
||||
m_xe2-=dx;
|
||||
//---
|
||||
FlameSet();
|
||||
}
|
||||
}
|
||||
//--- start timer
|
||||
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Generate array that describes the body of flame |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameCreate(void)
|
||||
{
|
||||
static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}};
|
||||
static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}};
|
||||
//---
|
||||
double a=rand(); // parameter of line a*x+b
|
||||
double b=rand(); // parameter of line a*x+b
|
||||
double c=rand(); // parameter of sine c*Sin(d*x)
|
||||
double d=rand(); // parameter of sine c*Sin(d*x)
|
||||
int w=rand(); // width at the base
|
||||
int l=rand(); // length
|
||||
//--- normalize
|
||||
a=fmod(a,(m_a2-m_a1))+m_a1;
|
||||
b=(m_yb1+m_yb2)/2;
|
||||
c=fmod(c,20);
|
||||
d=fmod(d,3*M_PI)+M_PI;
|
||||
//--- shape
|
||||
w%=150;
|
||||
if(w<10)
|
||||
w=10; // but no less than 10
|
||||
sword[1].size=w;
|
||||
w=rand();
|
||||
l%=50;
|
||||
sword[1].pos=l+30;
|
||||
l=rand();
|
||||
//--- sizes
|
||||
w=(m_yb2-m_yb1!=0) ? w%(m_yb2-m_yb1) : 10; // proportional to the starting width
|
||||
if(w<10)
|
||||
w=10; // but no less than 10
|
||||
l=l%((m_xe1-m_xb1)/(int)m_bar_gap-20)+20; // proportional to length
|
||||
//--- create
|
||||
int total=ArraySize(m_cloud_axis);
|
||||
for(int i=0;i<total;i++)
|
||||
m_cloud_axis[i]=int(a*i+b+c*sin(i/d));
|
||||
//--- draw
|
||||
FlameDraw(w,l,sword,flame);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculates and renders frame |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::FlameCalculate(void)
|
||||
{
|
||||
//--- calculate new frame
|
||||
int c;
|
||||
int idx;
|
||||
//--- draw body of flame to the right
|
||||
for(int x=0,x_tot=m_width-1;x<x_tot;x++)
|
||||
{
|
||||
//--- separately for y==0
|
||||
c=m_flame[x]+m_flame[x+m_width];
|
||||
c+=+m_flame[x]+m_flame[x+m_width];
|
||||
m_flame[x]=uchar(c/4);
|
||||
//---
|
||||
for(int y=1,y_tot=m_height-1;y<y_tot;y++)
|
||||
{
|
||||
idx=y*m_width+x;
|
||||
c=m_flame[idx-m_width]+m_flame[idx]+m_flame[idx+m_width];
|
||||
idx++;
|
||||
c+=m_flame[idx-m_width]+m_flame[idx]+m_flame[idx+m_width];
|
||||
m_flame[idx]=uchar(c/6);
|
||||
}
|
||||
//--- separately for y==m_height-1
|
||||
idx=(m_height-1)*m_width+x;
|
||||
c=m_flame[idx-m_width]+m_flame[idx];
|
||||
idx++;
|
||||
c+=m_flame[idx-m_width]+m_flame[idx];
|
||||
m_flame[idx]=uchar(c/4);
|
||||
}
|
||||
//--- move flame to the resource buffer
|
||||
for(int y=0;y<m_height;y++)
|
||||
{
|
||||
for(int x=0;x<m_width;x++)
|
||||
{
|
||||
idx=y*m_width+x;
|
||||
m_pixels[idx]=m_palette[m_flame[idx]];
|
||||
}
|
||||
}
|
||||
//---
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws "cloud" |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::CloudDraw(const double &prices[],const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[],const uchar t_level,const bool custom_gradient)
|
||||
{
|
||||
//--- check
|
||||
int total=ArraySize(prices);
|
||||
if(total<2)
|
||||
return;
|
||||
if(total>lenght)
|
||||
total=lenght;
|
||||
//--- draw
|
||||
int xb,xe; // coordinates of the segment
|
||||
int ybm,yem; // coordinates of the center line
|
||||
int yb1,ye1; // coordinates of the first line
|
||||
int yb2,ye2; // coordinates of the second line
|
||||
int xx;
|
||||
//--- for implementation of variable width
|
||||
int w_total=ArraySize(size);
|
||||
if(w_total<2)
|
||||
return;
|
||||
int w_i =0;
|
||||
int w_is=(int)size[w_i].pos*total/100;
|
||||
int w_ie=(int)size[w_i+1].pos*total/100;
|
||||
double w =size[w_i].size*width/100;
|
||||
double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
||||
//--- draw from left to right
|
||||
xb=0;
|
||||
ChartTimePriceToXY(0,0,0,prices[0],xx,ybm);
|
||||
yb1=ybm-(int)(w/2);
|
||||
yb2=ybm+(int)(w/2);
|
||||
//--- draw
|
||||
for(int i=1;i<total;i++)
|
||||
{
|
||||
xe=(int)(i*m_bar_gap);
|
||||
if(prices[i]==DBL_MAX)
|
||||
continue;
|
||||
ChartTimePriceToXY(0,0,0,prices[i],xx,yem);
|
||||
w+=dw;
|
||||
ye1=yem-(int)(w/2);
|
||||
ye2=yem+(int)(w/2);
|
||||
//--- draw the segment of 'cloud'
|
||||
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
||||
xb=xe;
|
||||
if(xb>=m_width)
|
||||
break;
|
||||
yb1=ye1;
|
||||
yb2=ye2;
|
||||
while(i>=w_ie-1 && i!=total-1)
|
||||
{
|
||||
w_i++;
|
||||
w_is=(int)size[w_i].pos*total/100;
|
||||
w_ie=(int)size[w_i+1].pos*total/100;
|
||||
w =size[w_i].size*width/100;
|
||||
if(w_ie==w_is)
|
||||
{
|
||||
//--- for "instant" resize
|
||||
dw=size[w_i+1].size*width/100-w;
|
||||
w+=dw;
|
||||
ye1=yem-(int)(w/2);
|
||||
ye2=yem+(int)(w/2);
|
||||
//--- draw the segment of 'cloud'
|
||||
GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient);
|
||||
yb1=ye1;
|
||||
yb2=ye2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws area with vertical fill using specified gradient |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[])
|
||||
{
|
||||
//--- it is assumed that the colors array has sufficient size and positions are already sorted in ascending order
|
||||
//--- get length by X and Y
|
||||
int x1 =xb;
|
||||
int y1 =yb1;
|
||||
int x2 =xb;
|
||||
int y2 =yb2;
|
||||
int dx =(xe>xb)? xe-xb : xb-xe;
|
||||
int dy1=(ye1>yb1)? ye1-yb1 : yb1-ye1;
|
||||
int dy2=(ye2>yb2)? ye2-yb2 : yb2-ye2;
|
||||
//--- get direction by X and Y
|
||||
int sx =(xb<xe)? 1 : -1;
|
||||
int sy1=(yb1<ye1)? 1 : -1;
|
||||
int sy2=(yb2<ye2)? 1 : -1;
|
||||
int er1=dx-dy1;
|
||||
int er2=dx-dy2;
|
||||
//--- extreme colors
|
||||
uint clr_first=gradient[0].clr;
|
||||
uint clr_last =gradient[ArraySize(gradient)-1].clr;
|
||||
//--- draw the first line
|
||||
while(x1!=xe || y1!=ye1)
|
||||
{
|
||||
//--- calculate coordinates of next pixel of the first line
|
||||
if((er1<<1)>-dy1)
|
||||
{
|
||||
//--- try to change X coordinate of the first line
|
||||
//--- draw the second line
|
||||
while(x2!=xe || y2!=ye2)
|
||||
{
|
||||
//--- calculate coordinates of next pixel of the second line
|
||||
if((er2<<1)>-dy2)
|
||||
{
|
||||
//--- try to change X coordinate of the second line
|
||||
//--- gradient fill
|
||||
GradientVerticalLine(x1,y1,y2,gradient);
|
||||
er2-=dy2;
|
||||
if(x2!=xe)
|
||||
x2+=sx;
|
||||
}
|
||||
if((er2<<1)<dx)
|
||||
{
|
||||
er2+=dx;
|
||||
if(y2!=ye2)
|
||||
y2+=sy2;
|
||||
}
|
||||
//--- draw the first line
|
||||
if(x1!=x2)
|
||||
break;
|
||||
}
|
||||
er1-=dy1;
|
||||
if(x1!=xe)
|
||||
x1+=sx;
|
||||
}
|
||||
if((er1<<1)<dx)
|
||||
{
|
||||
er1+=dx;
|
||||
if(y1!=ye1)
|
||||
y1+=sy1;
|
||||
}
|
||||
}
|
||||
//--- gradient fill
|
||||
GradientVerticalLine(x1,ye1,ye2,gradient);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws gradient vertical line |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::GradientVerticalLineMonochrome(int x,int y1,int y2,uint clr1,uint clr2)
|
||||
{
|
||||
//---
|
||||
double dc;
|
||||
int dd,dy=y2-y1;
|
||||
//--- check
|
||||
if(dy==0)
|
||||
return;
|
||||
//--- extract components from the first color
|
||||
uchar clr=(uchar)clr1;
|
||||
//--- parameters of pixels iteration
|
||||
if(dy>0)
|
||||
{
|
||||
dd=dy;
|
||||
dy=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dd=-dy;
|
||||
dy=-1;
|
||||
}
|
||||
//--- increments for the color components
|
||||
dc=(double)((uchar)clr2-clr)/dd;
|
||||
//--- draw
|
||||
for(int i=0;y1!=y2;i++,y1+=dy)
|
||||
{
|
||||
int idx=y1*m_width+x;
|
||||
//--- check range
|
||||
if(idx<0 || idx>=ArraySize(m_flame))
|
||||
continue;
|
||||
if(x>=0 && x<m_width && y1>=0 && y1<m_height)
|
||||
if(m_flame[idx]<(uchar)(clr+dc*i))
|
||||
m_flame[idx]=(uchar)(clr+dc*i);
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draws vertical line with specified gradient |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::GradientVerticalLine(int x,int y1,int y2,const GRADIENT_COLOR &gradient[])
|
||||
{
|
||||
//--- it is assumed that the colors array has sufficient size and positions are already sorted in ascending order
|
||||
int total=ArraySize(gradient);
|
||||
int dy=y2-y1;
|
||||
//--- draw segments
|
||||
for(int i=0;i<total-1;i++)
|
||||
GradientVerticalLineMonochrome(x,y1+dy*gradient[i].pos/100,y1+dy*gradient[i+1].pos/100,gradient[i].clr,gradient[i+1].clr);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Event handler |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::ChartEventHandler(const int id,const long &lparam,const double &dparam,const string &sparam)
|
||||
{
|
||||
//--- events filter
|
||||
switch(id)
|
||||
{
|
||||
case CHARTEVENT_CHART_CHANGE:
|
||||
//--- handle only chart modification events
|
||||
if(m_chart_scale!=(uint)ChartGetInteger(0,CHART_SCALE))
|
||||
{
|
||||
Delay(20);
|
||||
//--- changed horizontal scale
|
||||
ChartScale();
|
||||
if(m_pb1!=0.0)
|
||||
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
||||
return;
|
||||
}
|
||||
if(m_height!=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS))
|
||||
{
|
||||
//--- changed vertical size
|
||||
Delay(20);
|
||||
if(m_pb1!=0.0)
|
||||
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
||||
return;
|
||||
}
|
||||
if(m_chart_price_min!=ChartGetDouble(0,CHART_PRICE_MIN) ||
|
||||
m_chart_price_max!=ChartGetDouble(0,CHART_PRICE_MAX))
|
||||
{
|
||||
//--- changed vertical scale
|
||||
Delay(20);
|
||||
if(m_pb1!=0.0)
|
||||
FlameSet(m_tb1,m_pb1,m_te1,m_pe1,m_tb2,m_pb2,m_te2,m_pe2);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
//--- organize custom timer
|
||||
case CHARTEVENT_CUSTOM+1302:
|
||||
//--- time to draw the new frame?
|
||||
if(GetTickCount()>m_time_redraw)
|
||||
{
|
||||
//--- add the body of flame
|
||||
FlameCreate();
|
||||
//--- draw frame
|
||||
FlameCalculate();
|
||||
Update();
|
||||
//--- calculate time for the next frame
|
||||
m_time_redraw=GetTickCount()+m_delay;
|
||||
}
|
||||
//--- generate next event for custom timer
|
||||
EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Delay |
|
||||
//+------------------------------------------------------------------+
|
||||
void CFlameCanvas::Delay(const uint value)
|
||||
{
|
||||
//--- too small
|
||||
if(value<10)
|
||||
return;
|
||||
//--- start delay
|
||||
uint cnt=GetTickCount()+value;
|
||||
//--- delay
|
||||
while(cnt>=GetTickCount());
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user