Тестируем CGraphic - вопросы и предложения - страница 6

 
Roman Konopelko:
В классе CGraphic заменил во всех местах тип color на uint, как вы и просили.

Так же добавил новые методы в класс CCanvas, которые позволяют рисовать примитивы с заданной толщиной:
В соответствия с нововведение CCanvas расширил свойства CCurve:
Теперь при отрисовки кривой линиями можно задавать толщину линий и стиль её концов.


Да это же просто офигительно.
 

Была пересмотрена работа со сплайнами (интерполяция кривыми Безье). Её реализация была перенесена из класса CGraphics непосредственно в CCanvas, что позволяет строить сплайны вне библиотеки Graphics.‌

Кроме того был добавлен алгоритм отрисовки замкнутого сплайна.

В итоге в классе CCanvas появилось два новый открытых метода:‌

void              PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,
                                    ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                    double tension=0.5,double step=10);
void              PolygoneSmooth(int &x[],int &y[],const uint clr,const int size,
                                    ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                    double tension=0.5,double step=10);

Данные методы позволяет рисовать сплайны в заданном стиле и с заданной толщиной.

В следствие того, что кривые Безье довольно точно описываю круг и эллипс, нет явной необходимости дополнять класс CCanvas новыми методами для отрисовки этих примитивов с заданной толщиной.

Пример аппроксимации эллипса‌ кривыми Безье на основе метода PolygoneSmooth:

#include <Canvas\Canvas.mqh>
//+------------------------------------------------------------------+
//| Get arrays with ellipse coordinates                              |
//+------------------------------------------------------------------+
void Ellipse(int &x[],int &y[])
  {
   int xc = 750;
   int yc = 300;
   int rx = 300;
   int ry = 150;
   ArrayResize(x,16);
   ArrayResize(y,16);
   int i=0;
   for(double fi=0; fi<2*M_PI; fi+=M_PI_4/2,i++)
     {
      x[i]=(int)MathRound(xc+cos(fi)*rx);
      y[i]=(int)MathRound(yc+sin(fi)*ry);
     }
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CCanvas canvas;
   canvas.CreateBitmapLabel(0,0,"Canvas",0,0,1500,600);
   canvas.Erase(ColorToARGB(clrWhite));
   int x[];
   int y[]; 
   Ellipse(x,y);
   canvas.PolygoneSmooth(x,y,ColorToARGB(clrBlack),20,STYLE_SOLID,LINE_END_BUTT,0.5,1);
   canvas.Arc(750,300,300,150,0,M_PI*2,ColorToARGB(clrRed));   
   canvas.Update();
  }

Результат:

 

Еще один возможный шаг в сторону универсальности библиотеки Graphics: пользовательский режим отрисовки кривых CURVE_CUSTOM.

Данный режим избавит от необходимости делать наследников класса ‌CGraphic и перегружать методы ...Plot, что бы нарисовать кривую иначе, чем позволяют стандартные средства библиотеки.

Для реализации данного режима CURVE_CUSTOM в класс CCurve будут добавлены новые свойства:‌

   //--- gets or sets the custom properties
   PlotFucntion      CustomPlotFunction(void)              const { return(m_custom_plot_func);   }
   void             *CustomPlotCBData(void)                const { return(m_custom_plot_cbdata); }
   void              CustomPlotFunction(PlotFucntion func) { m_custom_plot_func=func;     }
   void              CustomPlotCBData(void *cbdata)        { m_custom_plot_cbdata=cbdata; }

Основывается он на новом указателе на функцию PlotFucntion:‌

typedef void(*PlotFucntion)(double &x[],double &y[], int size, CGraphic *graphic,CCanvas *canvas,void *cbdata);

Данный подход открывает новые возможности для отрисовки графиков.

Для примера реализуем построение свечей‌ в библиотеки CGraphics:

1. Создадим класс-конте‌йнер в котором будут храниться все данные об одной свече:‌

//+------------------------------------------------------------------+
//| Class CCandle                                                    |
//| Usage: class to represent the candle                             |
//+------------------------------------------------------------------+
class CCandle: public CObject
  {
private:
   double            m_open;
   double            m_close;
   double            m_high;
   double            m_low;
   uint              m_clr_inc;
   uint              m_clr_dec;
   int               m_width;

public:
                     CCandle(const double open,const double close,const double high,const double low,
                                                       const int width,const uint clr_inc=0x000000,const uint clr_dec=0xF5F5F5);
                    ~CCandle(void);
   double            OpenValue(void)            const { return(m_open);     }
   double            CloseValue(void)           const { return(m_close);    }
   double            HigthValue(void)           const { return(m_high);     }
   double            LowValue(void)             const { return(m_low);      }
   uint              CandleColorIncrement(void) const { return(m_clr_inc);  }
   uint              CandleColorDecrement(void) const { return(m_clr_dec);  }
   int               CandleWidth(void)          const { return(m_width);    }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCandle::CCandle(const double open,const double close,const double high,const double low,
                                 const int width,const uint clr_inc=0x000000,const uint clr_dec=0xF5F5F5):
                                 m_open(open),m_close(close),m_high(high),m_low(low),
                                 m_clr_inc(clr_inc),m_clr_dec(clr_dec),m_width(width)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCandle::~CCandle(void)
  {
  }

2. Так как класс CCandle является наследником от CObject, то все свечи которые мы хотим нарисовать мы можем последовательно записать в объект класса CArrayObj. Этот массив будет попадать в наш пользовательский метод отрисовки как параметр cbdata. В итоге метод по отрисовки свечей будет выглядеть следующим образом:‌

//+------------------------------------------------------------------+
//| Custom method for plot candles                                   |
//+------------------------------------------------------------------+
void PlotCandles(double &x[],double &y[],int size,CGraphic *graphic,CCanvas *canvas,void *cbdata)
  {
//--- check obj
   CArrayObj *candles=dynamic_cast<CArrayObj*>(cbdata);
   if(candles==NULL || candles.Total()!=size)
      return;
//--- plot candles  
   for(int i=0; i<size; i++)
     {
      CCandle *candle=dynamic_cast<CCandle*>(candles.At(i));
      if(candle==NULL)
         return;
      //--- primary calculate
      int xc=graphic.ScaleX(x[i]);
      int width_2=candle.CandleWidth()/2;
      int open=graphic.ScaleY(candle.OpenValue());
      int close=graphic.ScaleY(candle.CloseValue());
      int high=graphic.ScaleY(candle.HigthValue());
      int low=graphic.ScaleY(candle.LowValue());
      uint clr=(open<=close) ? candle.CandleColorIncrement() :  candle.CandleColorDecrement();
      //--- plot candle
      canvas.LineVertical(xc,high,low,0x000000);
      //--- plot candle real body
      canvas.FillRectangle(xc+width_2,open,xc-width_2,close,clr);
      canvas.Rectangle(xc+width_2,open,xc-width_2,close,0x000000);
     }
  }

‌‌‌3. Для простоты, все свечи будем генерироваться случайным образом. И так мы последовательно генерируем 10 свечей и наполняем им объект класса CArrayObj. После создаём объект CGraphics и добавляем в него одну кривую при этом указывая, что её отрисовка будет происходить на основе наше функции PlotCandles. Так же необходимо изменить максимальное и минимальное значение по оси y, так что бы наши свечи были полностью видны.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int count=10;
   int width=10;
   double x[];
   double y[];
   ArrayResize(x,count);
   ArrayResize(y,count);
   CArrayObj candles();
   double max=0;
   double min=0;
//--- create values 
   for(int i=0; i<count; i++)
     {
      x[i] = i;
      y[i] = i;
      //--- calculate values
      double open=MathRound(50.0+(MathRand()/32767.0)*50.0);
      double close=MathRound(50.0+(MathRand()/32767.0)*50.0);
      double high=MathRound(MathMax(open,close)+(MathRand()/32767.0)*10.0);
      double low=MathRound(MathMin(open,close) -(MathRand()/32767.0)*10.0);
      //--- find max and min
      if(i==0 || max<high)
         max=high;
      if(i==0 || min>low)
         min=low;
      //--- create candle
      CCandle *candle=new CCandle(open,close,high,low,width);
      candles.Add(candle);
     }
//--- create graphic
   CGraphic graphic;
   if(!graphic.Create(0,"CandleGraphic",0,30,30,780,380))
     {
      graphic.Attach(0,"CandleGraphic");
     }
//--- create curve
   CCurve *curve=graphic.CurveAdd(x,y,CURVE_CUSTOM,"Candles");
//--- sets the curve properties
   curve.CustomPlotFunction(PlotCandles);
   curve.CustomPlotCBData(GetPointer(candles));
//--- sets the graphic properties
   graphic.YAxis().Max((int)max);
   graphic.YAxis().Min((int)min);
//--- plot 
   graphic.CurvePlotAll();
   graphic.Update();
  }

В результате мы получаем следующий график:


Файлы:
Canvas.mqh  304 kb
Axis.mqh  12 kb
Curve.mqh  23 kb
Graphic.mqh  73 kb
Candle.mq5  6 kb
 

@Roman Konopelko

есть небольш‌ая ошибка в функции CGraphic::SetDefaultParameters

Должны быть инициализированы цвета с учетом непрозрачности.

void CGraphic::SetDefaultParameters(void)
  {
...
...
//--- sets the default values for grid
   m_grid.clr_line=ColorToARGB(clrWhiteSmoke,255);
   m_grid.clr_axis_line=ColorToARGB(clrSilver,255);
 
o_o:

@Roman Konopelko

есть небольш‌ая ошибка в функции CGraphic::SetDefaultParameters

Должны быть инициализированы цвета с учетом непрозрачности.

Добрый день, я уже исправил этот момент, но к сожалению эти правки пока не попали в билд
 

Вот такой пример привёл к зависанию компьютера. Что делал: после навешивания индикатора на график в редакторе игрался различными комбинациями закомментировать/раскомментировать строки 87 и 88 (когда по одной, когда вместе)

//+------------------------------------------------------------------+
//|                                        tst.mq5 |
//|                              Copyright © 2017, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2017, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"
#property description "Panel indicator: \"Evening Star\" pattern " 
#property description "search results for different periods" 
#property indicator_chart_window 
#property indicator_buffers 0
#property indicator_plots   0
#include <Graphics\Graphic.mqh>
//--- object for creating graphs
CGraphic my_graphic;
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
bool           m_first_start=false;
//+------------------------------------------------------------------+ 
//| Custom indicator initialization function                         | 
//+------------------------------------------------------------------+ 
int OnInit()
  {
   if(!EventSetTimer(3))
      if(!EventSetTimer(3))
         if(!EventSetTimer(3))
           {
            Print("Error create timer! THREE attempts!");
            return(INIT_FAILED);
           }
//--- canvas creation
   my_graphic.Create(0,"Evening Star Statistics",0,10,10,800,550);

   my_graphic.CurvePlotAll();
   my_graphic.Update();
//---
   m_first_start=false;
//--- 
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   my_graphic.Destroy();
  }
//+------------------------------------------------------------------+ 
//| Custom indicator iteration function                              | 
//+------------------------------------------------------------------+ 
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   if(!m_first_start)
     {
      //--- revert access to arrays - do it like in timeseries 
      ArraySetAsSeries(time,true);
      ArraySetAsSeries(open,true);
      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);
      ArraySetAsSeries(close,true);

      int size=ArraySize(time);
      double arrY[],arrX[];
      ArrayResize(arrX,size);
      ArrayResize(arrY,size);
      for(int i=0; i<size;++i)
        {
         arrX[i]=(double)time[i];
         arrY[i]=close[i];
        }
      CCurve *curve_b=my_graphic.CurveAdd(arrX,arrY,CURVE_LINES,"Close");
      CAxis *xAxis=my_graphic.XAxis();           // получаем ось X
      //---попеременно комбинирую (комментировать, раскомментировать) строки 87
      //--- и 88 можно словить момент поглощения памяти и зависания компьютера
      //xAxis.AutoScale(false); 
      //xAxis.Type(AXIS_TYPE_DATETIME); 
      my_graphic.CurvePlotAll();
      my_graphic.Update();
      m_first_start=true;
     }
//--- return value of prev_calculated for next call 
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {

  }
//+------------------------------------------------------------------+

Повторил достижение два раза. Жаль не записывал последовательность действий. Третий раз банально боюсь проверять.


Добавлено: билд 1607 х64

Файлы:
tst.mq5  8 kb
 
Vladimir Karputov:

Вот такой пример привёл к зависанию компьютера. Что делал: после навешивания индикатора на график в редакторе игрался различными комбинациями закомментировать/раскомментировать строки 87 и 88 (когда по одной, когда вместе)

Повторил достижение два раза. Жаль не записывал последовательность действий. Третий раз банально боюсь проверять.


Добавлено: билд 1607 х64


Сегодня повторил рекорд - мертвое зависание компьютера, успел увидеть рост потребления ОЗУ с 2 Гб до 5.5 Гб. Вроде успел закрыть график, но компьютер висит уже пять минут.

В этот раз ввел ограничение на размер массива - не более 300 элементов. Как видно не помогло 🤔

 

что дебаг говорит?

 
o_o:

что дебаг говорит?


Ж жебага не дошло, я делал так: навесил индикатор и раскоментировал/закоментировал одну или две строки и компилировал. В итоге пишу с планшета, ноут ушёл в аут..
 
Vladimir Karputov:

Ж жебага не дошло, я делал так: навесил индикатор и раскоментировал/закоментировал одну или две строки и компилировал. В итоге пишу с планшета, ноут ушёл в аут..


Так, ноут после жёсткой перезагрузки ожил, но желание дальше проводить губительный эксперимент пропало - на ноуте крутятся несколько советников, поэтому нет желания ловить зависон на целый час.

Причина обращения: