Test CGraphic - domande e suggerimenti - pagina 6

 
Roman Konopelko:
Nella classe CGraphic, ho sostituito il tipo di colore con uint in tutti i punti, come avete chiesto.

Inoltre, ho aggiunto nuovi metodi alla classe CCanvas, che permettono di disegnare primitive con spessore specificato:
In linea con l'innovazione di CCanvas, ho ampliato le proprietà di CCurve:
Quando si disegna una curva con linee, è ora possibile specificare lo spessore delle linee e lo stile delle sue estremità.


È semplicemente fantastico.
 

La gestione delle spline (interpolazione con curve di Bézier) è stata rivista. La sua implementazione è stata spostata dalla classe CGraphics direttamente in CCanvas, il che permette di costruire spline al di fuori della libreria Graphics.

Inoltre, è stato aggiunto un algoritmo per il rendering delle spline chiuse.

Di conseguenza, la classe CCanvas ha ora due nuovi metodi pubblici.

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

Questi metodi permettono di disegnare spline in un dato stile e con un dato spessore.

Dato che le curve di Bezier descrivono cerchi ed ellissi in modo piuttosto preciso, non c'è un'ovvia necessità di aumentare la classe CCanvas con nuovi metodi per rendere queste primitive con un dato spessore.

L'esempio di approssimazione dell'ellisse con curve di Bézier basato sul metodo 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();
  }

Risultato:

 

Un altro possibile passo verso la versatilità della libreria Graphics: una modalità di disegno di curve personalizzate CURVE_CUSTOM.

Questa modalità eliminerà la necessità di ereditare la classe CGraphic e di sovraccaricare i metodi ...Plot per disegnare una curva in modo diverso da quello consentito dagli strumenti standard della libreria.

Per implementare questa modalità CURVE_CUSTOM saranno aggiunte nuove proprietà alla 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; }

Si basa su un nuovo puntatore alla funzione PlotFucntion.

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

Questo approccio apre nuove possibilità per disegnare le trame.

Implementiamo il disegno di candele nella libreria CGraphics come esempio:

1. Creiamo una classe contenitore in cui saranno memorizzati tutti i dati di una singola candela.

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

Dato che la classe CCandle è una discendente della classe CObject, tutti i candelabri che vogliamo disegnare, possiamo scriverli sequenzialmente nell'oggetto della classe CArrayObj. Questo array entrerà nel nostro metodo di disegno personalizzato come parametro cbdata. Di conseguenza, il metodo di disegno delle candele avrà il seguente aspetto.

//+------------------------------------------------------------------+
//| 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. Per semplicità, tutti i candelieri saranno generati casualmente. E così generiamo sequenzialmente 10 candele e riempiamo l'oggetto di classe CArrayObj con esse. Poi creiamo l'oggetto CGraphics e aggiungiamo una curva in esso, specificando che sarà disegnata usando la nostra funzione PlotCandles. Dobbiamo anche cambiare i valori massimi e minimi dell'asse y, in modo che le nostre candele siano completamente visibili.

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

Come risultato, otteniamo il seguente grafico:


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

@Roman Konopelko

c'è un piccolo errore nella funzione CGraphic::SetDefaultParameters

I colori devono essere inizializzati tenendo conto dell'opacità.

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

c'è un piccolo errore nella funzione CGraphic::SetDefaultParameters

I colori devono essere inizializzati tenendo conto dell'opacità.

Buon pomeriggio, ho già sistemato questo punto, ma purtroppo queste modifiche non hanno ancora raggiunto la build
 

Questo esempio ha causato il blocco del computer. Cosa ho fatto: dopo aver messo l'indicatore sul grafico nell'editor, ho giocato con diverse combinazioni di commentare/non commentare le linee 87 e 88 (quando una alla volta, quando insieme)

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

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

Ripetuto il risultato due volte. Non ho registrato la sequenza delle azioni. La terza volta è banale che ho paura di controllare.


Aggiunto: build 1607 x64

File:
tst.mq5  8 kb
 
Vladimir Karputov:

Questo esempio ha causato il blocco del computer. Cosa ho fatto: dopo aver messo l'indicatore sul grafico nell'editor, ho giocato con diverse combinazioni di commentare/non commentare le linee 87 e 88 (quando una alla volta, quando insieme)

Ripetuto il risultato due volte. Non ho registrato la sequenza delle azioni. La terza volta è banale che ho paura di controllare.


Aggiunto: build 1607 x64


Ripetuto il record oggi - computer morto si blocca, è riuscito a vedere il consumo di RAM salire da 2GB a 5,5GB. Sembra che sia riuscito a chiudere il programma, ma il computer è rimasto sospeso per cinque minuti.

Questa volta ho messo un limite alla dimensione dell'array - non più di 300 elementi. Come puoi vedere non ha aiutato 🤔

 

cosa dice il debug?

 
o_o:

cosa dice il debug?


Jbug non l'ha capito, ho fatto così: ho passato il mouse su un indicatore e ho scomposto/commentato una o due linee e compilato. Ho finito per scrivere da un tablet, il portatile si è spento...
 
Vladimir Karputov:

Il gbug non ce l'ha fatta, ho fatto così: sono passato sopra l'indicatore e ho commentato/commentato una o due righe e compilato. Ho finito per scrivere da un tablet, il portatile si è spento...


Così, il portatile dopo il riavvio duro è tornato in vita, ma non voglio condurre ulteriori esperimenti distruttivi - ci sono diversi Expert Advisors in esecuzione sul portatile, quindi non c'è alcun desiderio di prendere un freeze per un'ora intera.