Teste CGraphic - perguntas e sugestões - página 6

 
Roman Konopelko:
Na classe CGraphic, eu substituí o tipo de cor por uint em todos os lugares, como você pediu.

Além disso, acrescentei novos métodos à classe CCanvas, que permitem desenhar primitivos com espessura especificada:
Em linha com a inovação do CCanvas, eu ampliei as propriedades do CCurve:
Ao desenhar uma curva com linhas, você pode agora especificar a espessura das linhas e o estilo de suas extremidades.


Isso é simplesmente fantástico.
 

A manipulação de estrias (interpolação com curvas de Bézier) foi revisada. Sua implementação foi transferida da classe CGraphics diretamente para o CCanvas, o que permite a construção de estrias fora da biblioteca gráfica.

Além disso, foi adicionado um algoritmo para renderizar spline fechado.

Como resultado, a classe CCanvas tem agora dois novos métodos públicos.

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);

Estes métodos permitem desenhar estrias em um determinado estilo e com uma determinada espessura.

Como as curvas de Bezier descrevem círculos e elipses de forma bastante precisa, não há necessidade óbvia de aumentar a classe CCanvas com novos métodos para tornar estes primitivos com uma determinada espessura.

O exemplo de aproximação de elipse por curvas Bézier baseadas no método 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();
  }

Resultado:

 

Outro passo possível para a versatilidade da biblioteca de gráficos: um modo de desenho de curvas personalizado CURVE_CUSTOM.

Este modo eliminará a necessidade de herdar a classe CGraphic e sobrecarregar os métodos de plotagem a fim de traçar uma curva diferente do que as ferramentas padrão da biblioteca permitem.

Para implementar este modo CURVE_CUSTOM novas propriedades serão adicionadas à classe 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; }

Ela se baseia em um novo ponteiro para a função PlotFucntion.

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

Esta abordagem abre novas possibilidades para o desenho de parcelas.

Vamos implementar o desenho de candelabros na biblioteca CGraphics como exemplo:

1. Vamos criar uma classe de contêineres na qual todos os dados de uma única vela serão armazenados.

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

Como a classe CCandle é descendente da classe CObject, todos os castiçais que queremos desenhar, podemos escrevê-los sequencialmente no objeto da classe CArrayObj. Esta matriz entrará em nosso método de desenho personalizado como o parâmetro cbdata. Como resultado, o método de desenho do castiçal será o seguinte.

//+------------------------------------------------------------------+
//| 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. Para simplificar, todos os castiçais serão gerados de forma aleatória. E assim geramos seqüencialmente 10 castiçais e preenchemos o objeto da classe CArrayObj com eles. Depois criamos o objeto CGraphics e adicionamos uma curva a ele, enquanto especificamos que ele será desenhado usando nossa função PlotCandles. Também precisamos mudar os valores máximos e mínimos do eixo y, para que nossas velas sejam totalmente visíveis.

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

Como resultado, obtemos o seguinte gráfico:


Arquivos anexados:
Canvas.mqh  304 kb
Axis.mqh  12 kb
Curve.mqh  23 kb
Graphic.mqh  73 kb
Candle.mq5  6 kb
 

@Roman Konopelko

há um pequeno erro na função CGraphic::SetDefaultParameters

As cores devem ser inicializadas com a opacidade levada em conta.

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

há um pequeno erro na função CGraphic::SetDefaultParameters

As cores devem ser inicializadas com a opacidade levada em conta.

Boa tarde, já resolvi este ponto, mas infelizmente estas edições ainda não chegaram ao fim.
 

Este exemplo causou o congelamento do computador. O que fiz: depois de colocar o indicador no gráfico no editor, joguei com diferentes combinações de linhas 87 e 88 (quando uma de cada vez, quando juntas)

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

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

Repetiu a conquista duas vezes. Eu não registrei a seqüência de ações. A terceira vez é trivial, tenho medo de verificar.


Adicionado: construir 1607 x64

Arquivos anexados:
tst.mq5  8 kb
 
Vladimir Karputov:

Este exemplo causou o congelamento do computador. O que fiz: depois de colocar o indicador no gráfico no editor, joguei com diferentes combinações de linhas 87 e 88 (quando uma de cada vez, quando juntas)

Repetiu a conquista duas vezes. Eu não registrei a seqüência de ações. A terceira vez é trivial, tenho medo de verificar.


Adicionado: construir 1607 x64


Repetimos o recorde hoje - computadores mortos pendurados, conseguiram ver o consumo de RAM aumentar de 2GB para 5,5GB. Parece ter conseguido fechar o cronograma, mas o computador está pendurado há cinco minutos.

Desta vez coloquei um limite para o tamanho da matriz - não mais de 300 elementos. Como você pode ver, isso não ajudou 🤔

 

o que diz o debug?

 
o_o:

o que diz o debug?


Jbug não entendeu, eu fiz isto: pairou um indicador e não comentou/comentou uma ou duas linhas e compilou. Acabou escrevendo a partir de um tablet, o laptop saiu.
 
Vladimir Karputov:

O percevejo não conseguiu, eu fiz isto: passei o mouse sobre o indicador e comentei/comentei uma ou duas linhas e compilei. Acabou escrevendo a partir de um tablet, o laptop saiu.


Assim, o laptop depois de um reinício duro voltou à vida, mas não quero conduzir mais experiências destrutivas - há vários Expert Advisors rodando no laptop, então não há desejo de pegar um congelamento por uma hora inteira.