Prueba CGraphic - preguntas y sugerencias - página 6

 
Roman Konopelko:
En la clase CGraphic, he sustituido el tipo de color por uint en todos los lugares, como me pediste.

Además, he añadido nuevos métodos a la clase CCanvas, que permiten dibujar primitivas con un grosor determinado:
En línea con la innovación de CCanvas, he ampliado las propiedades de CCurve:
Al dibujar una curva con líneas, ahora puede especificar el grosor de las líneas y el estilo de sus extremos.


Eso es increíble.
 

Se ha revisado el manejo de las splines (interpolación con curvas de Bézier). Su implementación se ha trasladado de la clase CGraphics directamente a CCanvas, lo que permite construir splines fuera de la biblioteca Graphics.

Además, se ha añadido un algoritmo para renderizar splines cerrados.

Como resultado, la clase CCanvas tiene ahora dos nuevos 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);

Estos métodos permiten dibujar splines con un estilo y un grosor determinados.

Dado que las curvas de Bézier describen los círculos y las elipses con bastante precisión, no hay necesidad obvia de aumentar la clase CCanvas con nuevos métodos para renderizar estas primitivas con un grosor determinado.

Ejemplo de aproximación de una elipse mediante curvas de Bézier basado en el 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:

 

Otro posible paso hacia la versatilidad de la biblioteca gráfica: un modo de dibujo de curvas personalizado CURVE_CUSTOM.

Este modo eliminará la necesidad de heredar la clase CGraphic y sobrecargar los métodos ...Plot para dibujar una curva de forma diferente a la que permiten las herramientas estándar de la biblioteca.

Para implementar este modo CURVE_CUSTOM se añadirán nuevas propiedades a la clase 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; }

Se basa en un nuevo puntero a la función PlotFucntion.

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

Este enfoque abre nuevas posibilidades para dibujar parcelas.

Vamos a implementar el dibujo de velas en la biblioteca CGraphics como ejemplo:

1. Vamos a crear una clase contenedora en la que se almacenarán todos los datos de una sola vela.

//+------------------------------------------------------------------+
//| 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 la clase CCandle es descendiente de la clase CObject, todos los candelabros que queramos dibujar, podemos escribirlos secuencialmente en el objeto de la clase CArrayObj. Este array entrará en nuestro método de dibujo personalizado como parámetro cbdata. Como resultado, el método de dibujo de velas tendrá el siguiente aspecto.

//+------------------------------------------------------------------+
//| 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 los candelabros se generarán aleatoriamente. Y así generamos secuencialmente 10 velas y llenamos el objeto de clase CArrayObj con ellas. A continuación, creamos el objeto CGraphics y añadimos una curva en él, mientras especificamos que se dibujará utilizando nuestra función PlotCandles. También tenemos que cambiar los valores máximos y mínimos del eje y, para que nuestras velas sean totalmente visibles.

//+------------------------------------------------------------------+
//| 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, obtenemos el siguiente gráfico:


Archivos adjuntos:
Canvas.mqh  304 kb
Axis.mqh  12 kb
Curve.mqh  23 kb
Graphic.mqh  73 kb
Candle.mq5  6 kb
 

@Roman Konopelko

hay un pequeño error en la función CGraphic::SetDefaultParameters

Los colores deben ser inicializados teniendo en cuenta la opacidad.

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

hay un pequeño error en la función CGraphic::SetDefaultParameters

Los colores deben inicializarse teniendo en cuenta la opacidad.

Buenas tardes, ya he corregido este punto, pero desgraciadamente estas ediciones no han llegado a la compilación todavía
 

Este ejemplo hizo que el ordenador se congelara. Lo que hice: después de poner el indicador en el gráfico en el editor, jugué con diferentes combinaciones de comentar/descomentar las líneas 87 y 88 (cuando una a la vez, cuando 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()
  {

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

Repite el logro dos veces. No he registrado la secuencia de acciones. La tercera vez es trivial y me da miedo comprobarlo.


Añadido: build 1607 x64

Archivos adjuntos:
tst.mq5  8 kb
 
Vladimir Karputov:

Este ejemplo hizo que el ordenador se congelara. Lo que hice: después de poner el indicador en el gráfico en el editor, jugué con diferentes combinaciones de comentar/descomentar las líneas 87 y 88 (cuando una a la vez, cuando juntas)

Repite el logro dos veces. No he registrado la secuencia de acciones. La tercera vez es trivial y me da miedo comprobarlo.


Añadido: build 1607 x64


Hoy he repetido el récord: el ordenador se cuelga, y he conseguido que el consumo de RAM pase de 2 GB a 5,5 GB. Parece que ha conseguido cerrar el programa, pero el ordenador lleva colgado cinco minutos.

Esta vez he puesto un límite al tamaño de la matriz: no más de 300 elementos. Como puedes ver no sirvió de nada 🤔

 

¿qué dice la depuración?

 
o_o:

¿qué dice la depuración?


Jbug no lo consiguió, hice lo siguiente: pasé un indicador y descomenté/comenté una o dos líneas y compilé. Acabé escribiendo desde una tableta, el portátil se estropeó...
 
Vladimir Karputov:

El gbug no lo hizo, hice esto: pasé por encima del indicador y comenté/comenté una o dos líneas y compilé. Acabé escribiendo desde una tableta, el portátil se estropeó...


Entonces, el portátil después de un reinicio duro volvió a la vida, pero no quiero llevar a cabo más experimento destructivo - hay varios Asesores Expertos que se ejecutan en el portátil, por lo que no hay deseo de coger una congelación durante toda una hora.