//+---------------------------------------------------------------------+
//|                                               #_i_3D_ADX.mq4        |
//|                                    Copyright © 2009, TigraVK        |
//|                                                                     |
//|Индикатор текущий тренд на основе ADX в                              |
//|трехмерной диаграмме в основном окне графика торгового               |
//|инструмента. Построчно отбражаются направления тренда с пошагово     |
//|увеличивающимися периодами.                                          |
//|В результате получается трехмерная картинка веера ADX                |
//|В данной версии индикатора возможны режимы отображения               |
//| - направления тренда по уровням +DI и -DI;                          |
//| - абсолютного значения кривой MAIN;                                 |
//| - скорости роста или снижения  +DI отнисительно -DI.                |
//|                                                                     |
//|Координата Х на диаграмме - время в формате графика торгового        |
//|инструмента                                                          |
//|Координата Y на диаграмме - сверху вниз последовательное отображение |
//|ADX с различными параметрами                                         |
//|Координата Z отражена различными цветами в зависимости от ситуации   |
//|и положения кривых ADX в данной временнОй точке                      |
//+---------------------------------------------------------------------+
#property copyright "Copyright © 2009, TigraVK"
#property link      "tigravk@yandex.ru"

#property indicator_chart_window
#include <stdlib.mqh>

#define ELEMENTS 80           //количество отображаемых строк в диаграмме
extern int IndMode = 0;       //один из трех режимов отрисовки диаграмм: 0-Направление;1-абсолютное значение;2-баровая скорость изменения 
extern int Columns = 600;    //Количество отображаемых столбцов диаграммы
extern int Width = 1;         //Ширина столбца, в барах
extern int DrawShift = 0;     //Смещение нулевого столбца (в барах) относительно правой границы окна графика
                              //применяется в случае вывода двух копий индикатора с разными параметрами в одно окно
                              //для исключения визуальных пересечений диаграмм
                              //Например, для вывода второй диаграммы корректно будет установить DrawShift=Columns+4

//-- Начальные параметры iADX и приращение периода -----------
extern int ADXperiod = 3; 
extern int ADXperiod_step = 5;
extern int Method = MODE_SMA;
extern int ADXappliedPrice = 0;     //PRICE_CLOSE 0 Цена закрытия 
                                    //PRICE_OPEN 1 Цена открытия 
                                    //PRICE_HIGH 2 Максимальная цена 
                                    //PRICE_LOW 3 Минимальная цена 
                                    //PRICE_MEDIAN 4 Средняя цена, (high+low)/2 
                                    //PRICE_TYPICAL 5 Типичная цена, (high+low+close)/3 
                                    //PRICE_WEIGHTED 6 Взвешенная цена закрытия, (high+low+close+close)/4 
//-------------------------------

#define COLUMNS_DOP 3   //кол-во дополнительных колонок в массиве ADX[][] (типа, вычислительный запас)
#define HEADER 6        //место под заголовок (измеряется высотой элемета диаграммы)
#define GAP 0           //пробел (в барах) между элементами диаграммы в горизонтали
#define GAP_PERCENT 20  //пробел (в процентах от высоты элемента диаграммы) между элементами диаграммы в вертикали

double ADXMAIN[][ELEMENTS];//массив карта кривой MAIN 
double ADXPLUS[][ELEMENTS];//массив карта кривой +DI
double ADXMINUS[][ELEMENTS];//массив карта кривой -DI
double Top;
double Bottom;
int    FreeBars;
string el_name;
string indname = "#_i_3D_ADX";
datetime tm;
int shift;

//+------------------------------------------------------------------+
int init()
{
   tm=Time[0];
   shift=DrawShift*(Width+GAP);
   Top    =WindowPriceMax(0);
   Bottom =WindowPriceMin(0);
   FreeBars=WindowBarsPerChart()-WindowFirstVisibleBar()-shift;
   if(IndMode==0)
   {
      el_name = "ADXDIR";
      CreateHeader("ADX direction", Top, Width, Columns, FreeBars, Yellow);
   }
   else if(IndMode==1)
   {
      el_name = "ADXABS";
      CreateHeader("ADX absolute", Top, Width, Columns, FreeBars, Yellow);
   }
   else if(IndMode==2)
   {
      el_name = "ADXACC";
      CreateHeader("ADX acceleration", Top, Width, Columns, FreeBars, Yellow);
   }
   else
   {
      el_name = "ADX";
      CreateHeader("NONE", Top, Width, Columns, FreeBars, Yellow);
   }
   
   IndicatorShortName(indname);
   
   ArrayResize(ADXMAIN,Columns+COLUMNS_DOP);
   ArrayInitialize(ADXMAIN, 0.0);
   
   ArrayResize(ADXPLUS,Columns+COLUMNS_DOP);
   ArrayInitialize(ADXPLUS, 0.0);
   
   ArrayResize(ADXMINUS,Columns+COLUMNS_DOP);
   ArrayInitialize(ADXMINUS, 0.0);
   
   //создаем квадратики
   for(int i=0; i<Columns; i++)
      CreateShield(el_name+i+"_", Top, Bottom, Width, FreeBars-(Width+GAP)*i, Gray);

   return(0);
}
//+------------------------------------------------------------------+
int deinit()
{
   DeleteIndicatorObject(el_name);
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   int i;
   int limit;
   double top    =WindowPriceMax(0);
   double bottom =WindowPriceMin(0);
   int free_bars =WindowBarsPerChart()-WindowFirstVisibleBar()-shift;
   
   int counted_bars=IndicatorCounted();
   limit=Bars-counted_bars;
   if(limit>Columns+COLUMNS_DOP) limit=Columns+COLUMNS_DOP;


   //Процедура перерисовки диаграммы при смене текущего бара или размеров экранной области графика
   if(Top!=top || Bottom!=bottom || FreeBars!=free_bars || tm!=Time[0])
   {
      Top=top; Bottom=bottom; FreeBars=free_bars; tm=Time[0];
      for(i=0; i<Columns; i++)
      {
         if(MoveShield(el_name+i+"_", Top, Bottom, Width, FreeBars-(Width+GAP)*i)<0)
         {
            DeleteIndicatorObject(el_name+i+"_");
            CreateShield(el_name+i+"_", Top, Bottom, Width, FreeBars-(Width+GAP)*i, Gray);
         }
      }
      MoveHeader(Top, Width, Columns, FreeBars, Yellow);
   }



   if(limit>=Columns+COLUMNS_DOP) //полная отрисовка при запуске индикатора
   {
      AdxDATA_fill(ADXMAIN, ADXPLUS, ADXMINUS, limit, 0, 0);
      if(IndMode==0)      AdxWeerDirection(ADXPLUS, ADXMINUS, Columns);
      else if(IndMode==1) AdxWeerAbsolute(ADXMAIN, Columns);
      else if(IndMode==2) AdxWeerAcceleration(ADXPLUS, ADXMINUS, Columns);
   }
   else if(limit>1 && limit<Columns+COLUMNS_DOP) //перерисовка при смене бара
   {
      AdxDATA_leftShift(ADXMAIN, ADXPLUS, ADXMINUS, Columns+COLUMNS_DOP, limit-1);
      AdxDATA_fill(ADXMAIN, ADXPLUS, ADXMINUS, limit-1, 0, 0);
      if(IndMode==0)      AdxWeerDirection(ADXPLUS, ADXMINUS, Columns);
      else if(IndMode==1) AdxWeerAbsolute(ADXMAIN, Columns);
      else if(IndMode==2) AdxWeerAcceleration(ADXPLUS, ADXMINUS, Columns);
   }
   else //перерисовка только текущего бара
   {
      AdxDATA_fill(ADXMAIN, ADXPLUS, ADXMINUS, limit, 0, 0);
      if(IndMode==0)      AdxWeerDirection(ADXPLUS, ADXMINUS, limit);
      else if(IndMode==1) AdxWeerAbsolute(ADXMAIN, limit);
      else if(IndMode==2) AdxWeerAcceleration(ADXPLUS, ADXMINUS, Columns);
   }
   err("Start:  ");
   return(0);
}


//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
void AdxDATA_fill(double& MAIN[][], double& PDI[][], double& MDI[][], int recount_columns, int start_columns, int historyBarShift)
{
   int i,j;
   int adxp;
   
   for(j=start_columns; j<recount_columns+start_columns; j++)
   {
      adxp=ADXperiod;
      for(i=0; i<ELEMENTS; i++)
      {
         PDI[j][i] =iADX(NULL,0,adxp,ADXappliedPrice,MODE_PLUSDI,j+historyBarShift);
         MDI[j][i] =iADX(NULL,0,adxp,ADXappliedPrice,MODE_MINUSDI,j+historyBarShift);
         MAIN[j][i]=iADX(NULL,0,adxp,ADXappliedPrice,MODE_MAIN,j+historyBarShift);
         adxp=adxp+ADXperiod_step;
      }
   }
   return;
}

//+------------------------------------------------------------------+
void AdxDATA_leftShift(double& MAIN[][], double& PDI[][], double& MDI[][], int columns, int shift_columns_count)
{
   int i,j,k;
   for(k=1; k<=shift_columns_count; k++)
   {   
      for(j=columns-1; j>0; j--)
      {
         for(i=0; i<ELEMENTS; i++)
         {
            MAIN[j][i] = MAIN[j-1][i];
            PDI[j][i] = PDI[j-1][i];
            MDI[j][i] = MDI[j-1][i];
         }
      }
      for(i=0; i<ELEMENTS; i++)
      {
         MAIN[j][i] = 0.0;
         PDI[j][i] = 0.0;
         MDI[j][i] = 0.0;
      }
   }
}

//+------------------------------------------------------------------+
// зеленый цвет, если +DI выше -DI
// красный цвет, если -DI выше +DI
void AdxWeerDirection(double& PDI[][], double& MDI[][], int recount_columns)
{
   int i,j;
   for(j=recount_columns-1; j>=0; j--)
   {
      for(i=0; i<ELEMENTS; i++)
      {
         if(PDI[j][i]-MDI[j][i]>=0.0)    ObjectSet(el_name+DoubleToStr(j,0)+"_"+i, OBJPROP_COLOR, LightGreen);
         else                            ObjectSet(el_name+DoubleToStr(j,0)+"_"+i, OBJPROP_COLOR, Red);
      }
   }
}


//+------------------------------------------------------------------+
// абсолютное значение выводится в градации от белого (минимум) до зеленого(максимум)
// Максимум нормируется к максимальному значению кривой за исследуемый период истории
void AdxWeerAbsolute(double& DATA[][], int columns)
{
   int i,j;
   color clr;
   double min, max;
     
   for(i=0; i<ELEMENTS; i++)
   {
      min=10000;
      max=-10000;
      for(j=0; j<columns; j++)
      {
         min=MathMin(min, MathAbs(DATA[j][i]));
         max=MathMax(max, MathAbs(DATA[j][i]));
      }
   
      for(j=0; j<columns; j++)
      {
            clr = ColorGradient(DATA[j][i], min, max, 1); //зеленый
            ObjectSet(el_name+j+"_"+i, OBJPROP_COLOR, clr);
      }
   }
}

//+------------------------------------------------------------------+
void AdxWeerAcceleration(double& PDI[][], double& MDI[][], int columns)
{
   int i,j;
   color clr;
   double max_vel;
   double VEL[][ELEMENTS];
   ArrayResize(VEL, columns);
     
   //Нарисуем матрицу текущих разностей +DI и -DI
   for(j=columns-1; j>=0; j--)
      for(i=0; i<ELEMENTS; i++)
         VEL[j][i]=PDI[j][i]-MDI[j][i];
   
   //за период Columns самая сильная абсолютная баровая скорость
   for(i=0; i<ELEMENTS; i++)
   {
      max_vel=-10000;
      for(j=0; j<columns; j++)
            max_vel=MathMax(max_vel, MathAbs(VEL[j][i]));
   
      //Скорость вниз - красный градиент; скорость вверх - зеленый
      for(j=0; j<columns; j++)
      {
         if(VEL[j][i]>=0)   
         {
            clr = ColorGradient(VEL[j][i], 0, max_vel, 1); //зеленый
            ObjectSet(el_name+j+"_"+i, OBJPROP_COLOR, clr);
         }
         else
         {
            clr = ColorGradient(-VEL[j][i], 0, max_vel, 0); //красный
            ObjectSet(el_name+j+"_"+i, OBJPROP_COLOR, clr);
         }
      }
   }
}


//+------------------------------------------------------------------+
color ColorGradient(double current_vol, double min_vol, double max_vol, int maincolor)
{
   int R,G,B;
   int cur;
   if(current_vol<min_vol) current_vol=min_vol;    
   if(current_vol>max_vol) current_vol=max_vol;    

   cur = 255-255*(current_vol-min_vol)/(max_vol-min_vol);
   if(maincolor==0)  //RED
   { R=255; G=cur; B=cur; }
   else if(maincolor==1) //GREEN
   { R=cur; G=255; B=cur; }
   else if(maincolor==2) //BLUE
   { R=cur; G=cur; B=255; }
   else {R=0;G=0;B=0;}   
   
   G<<=8;
   B<<=16;
   return(R+G+B);
}

//+------------------------------------------------------------------+
void CreateShield(string name_mask, double top, double bottom, int width, int freebars, color clr)
{
   double high, gap, y1, y2;
   datetime fut0, fut1;

   high = (top-bottom)/(ELEMENTS*1.0);
   gap= high*GAP_PERCENT/100.0;
   bottom=bottom+gap;
   top=top-HEADER*high;
   high = (top-bottom)/(ELEMENTS*1.0);
   gap= high*GAP_PERCENT/100.0;
   high=high-gap;
   
   if(freebars>0)
   {
      fut0=Time[0]+Period()*freebars*60;
      fut1=Time[width]+Period()*freebars*60;
   }
   else
   {
      fut0=iTime(NULL,0,-freebars);
      fut1=iTime(NULL,0,-freebars+width);
   }
   y2=top;
   for(int i=0; i<ELEMENTS; i++)
   {
      y1=y2-gap;
      y2=y1-high;
      ObjectCreate(name_mask+i, OBJ_RECTANGLE, 0, fut0, y1, fut1, y2);
      ObjectSet(name_mask+i, OBJPROP_COLOR, clr);
   }
   return;
}

//+------------------------------------------------------------------+
int MoveShield(string name_mask, double top, double bottom, int width, int freebars)
{
   double high, gap, y1, y2;
   datetime fut0, fut1;

   high = (top-bottom)/(ELEMENTS*1.0);
   gap= high*GAP_PERCENT/100.0;
   bottom=bottom+gap;
   top=top-HEADER*high;
   high = (top-bottom)/(ELEMENTS*1.0);
   gap= high*GAP_PERCENT/100.0;
   high=high-gap;
   
   if(freebars>0)
   {
      fut0=Time[0]+Period()*freebars*60;
      fut1=Time[width]+Period()*freebars*60;
   }
   else
   {
      fut0=iTime(NULL,0,-freebars);
      fut1=iTime(NULL,0,-freebars+width);
   }
   y2=top;
   for(int i=0; i<ELEMENTS; i++)
   {
      y1=y2-gap;
      y2=y1-high;
      if(ObjectMove(name_mask+i, 0, fut0, y1)==false) return(-1);
      ObjectMove(name_mask+i, 1, fut1, y2);
   }
   return(0);
}

//+------------------------------------------------------------------+
void CreateHeader(string text, double top, int width, int columns, int freebars, color clr)
{
   datetime time_center;
   int allbars;
   if(freebars<0) {Print("ERROR CreateHeader: freebars<0"); return;}   
   allbars = columns*(width+GAP); //столько нужно баров под диаграмму
   if(allbars/2<freebars) //центр заголовка умещается в зоне за графиком справа
      time_center=Time[0] + 60*Period()*(freebars - columns*(width+GAP)/2);
   else   //отрисовка частично захватывает историческую зону
      time_center = iTime(NULL, 0, allbars/2-freebars);
   ObjectCreate(el_name+"_HEAD", OBJ_TEXT, 0, time_center, top);
   ObjectSetText(el_name+"_HEAD", text, 12, "Areal", clr);
   return;
}

//+------------------------------------------------------------------+
void MoveHeader(double top, int width, int columns, int freebars, color clr)
{
   datetime time_center;
   int allbars;
   if(freebars<0) {Print("ERROR CreateHeader: freebars<0"); return;}   
   allbars = columns*(width+GAP); //столько нужно баров под диаграмму
   if(allbars/2<freebars) //центр заголовка умещается в зоне за графиком справа
      time_center=Time[0] + 60*Period()*(freebars - columns*(width+GAP)/2);
   else   //отрисовка частично захватывает историческую зону
      time_center = iTime(NULL, 0, allbars/2-freebars);
   ObjectMove(el_name+"_HEAD", 0, time_center, top);
}

//+------------------------------------------------------------------+
void DeleteIndicatorObject(string mask)
{
   int obj_total, i, out;
   string name;
   out=1;
   while(out>0)
   {   
      obj_total=ObjectsTotal();
      out=0;
      for(i=0;i<obj_total;i++)
      {
         name = ObjectName(i);
         if(StringFind(name,mask)>=0) { ObjectDelete(name); out++; }
      }
   }
}
//+------------------------------------------------------------------+
void err(string name)
{
   int err=GetLastError();
   if(err!=0)  Print(name," ERROR(",err,"): ",ErrorDescription(err));
}

