Prüfung von CGraphic - Fragen und Anregungen - Seite 6

 
Roman Konopelko:
In der Klasse CGraphic habe ich, wie von Ihnen gewünscht, den Farbtyp an allen Stellen durch uint ersetzt.

Außerdem habe ich der Klasse CCanvas neue Methoden hinzugefügt, die es ermöglichen, Primitive mit einer bestimmten Dicke zu zeichnen:
Im Einklang mit der Innovation von CCanvas habe ich die Eigenschaften von CCurve erweitert:
Wenn Sie eine Kurve mit Linien zeichnen, können Sie jetzt die Dicke der Linien und den Stil ihrer Enden festlegen.


Das ist einfach großartig.
 

Die Handhabung von Splines (Interpolation mit Bézier-Kurven) wurde überarbeitet. Seine Implementierung wurde von der Klasse CGraphics direkt in CCanvas verlagert, wodurch Splines außerhalb der Grafikbibliothek konstruiert werden können.

Außerdem wurde ein Algorithmus für das Rendern geschlossener Splines hinzugefügt.

Infolgedessen verfügt die Klasse CCanvas jetzt über zwei neue öffentliche Methoden.

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

Mit diesen Methoden können Splines in einem bestimmten Stil und mit einer bestimmten Dicke gezeichnet werden.

Da Bezier-Kurven Kreise und Ellipsen recht genau beschreiben, besteht keine offensichtliche Notwendigkeit, die Klasse CCanvas um neue Methoden zur Darstellung dieser Primitive mit einer bestimmten Dicke zu erweitern.

Beispiel für die Annäherung einer Ellipse durch Bézier-Kurven auf der Grundlage der Methode 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();
  }

Ergebnis:

 

Ein weiterer möglicher Schritt zur Vielseitigkeit der Grafikbibliothek: ein benutzerdefinierter Kurven-Zeichenmodus CURVE_CUSTOM.

Dieser Modus macht es überflüssig, die CGraphic-Klasse zu erben und die ...Plot-Methoden zu überladen, um eine Kurve anders zu zeichnen, als es die Standardwerkzeuge der Bibliothek erlauben.

Um diesen Modus CURVE_CUSTOM zu implementieren, werden der Klasse CCurve neue Eigenschaften hinzugefügt.

   //--- 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; }

Sie basiert auf einem neuen Zeiger auf die Funktion PlotFucntion.

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

Dieser Ansatz eröffnet neue Möglichkeiten für das Zeichnen von Plots.

Lassen Sie uns als Beispiel das Zeichnen von Kerzenständern in der CGraphics-Bibliothek implementieren:

1. Lassen Sie uns eine Containerklasse erstellen, in der alle Daten einer einzelnen Kerze gespeichert werden.

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

Da die Klasse CCandle ein Nachkomme der Klasse CObject ist, können wir alle Kerzen, die wir zeichnen wollen, nacheinander in das Objekt der Klasse CArrayObj schreiben. Dieses Array wird als cbdata-Parameter in unsere benutzerdefinierte Zeichenmethode übernommen. Die Candlestick-Zeichnungsmethode sieht dann wie folgt aus.

//+------------------------------------------------------------------+
//| 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. Der Einfachheit halber werden alle Candlesticks nach dem Zufallsprinzip generiert. Wir erzeugen also nacheinander 10 Candlesticks und füllen das Objekt der Klasse CArrayObj mit ihnen. Dann erstellen wir ein CGraphics-Objekt und fügen ihm eine Kurve hinzu, wobei wir angeben, dass sie mit unserer Funktion PlotCandles gezeichnet werden soll. Wir müssen auch die Höchst- und Mindestwerte der y-Achse ändern, damit unsere Kerzen vollständig sichtbar sind.

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

Daraus ergibt sich das folgende Diagramm:


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

@Roman Konopelko

Es gibt einen kleinen Fehler in der Funktion CGraphic::SetDefaultParameters

Bei der Initialisierung von Farben sollte die Deckkraft berücksichtigt werden.

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

Es gibt einen kleinen Fehler in der Funktion CGraphic::SetDefaultParameters

Bei der Initialisierung von Farben sollte die Deckkraft berücksichtigt werden.

Guten Tag, ich habe diesen Punkt bereits behoben, aber leider sind diese Änderungen noch nicht in den Build eingeflossen
 

Bei diesem Beispiel friert der Computer ein. Was ich getan habe: Nachdem ich den Indikator im Editor auf das Diagramm gesetzt hatte, spielte ich mit verschiedenen Kombinationen von kommentierenden/dekommentierenden Zeilen 87 und 88 (wenn eine auf einmal, wenn zusammen)

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

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

Wiederhole die Leistung zweimal. Ich habe die Reihenfolge der Aktionen nicht aufgezeichnet. Das dritte Mal ist trivial, ich habe Angst, es zu überprüfen.


Hinzugefügt: Build 1607 x64

Dateien:
tst.mq5  8 kb
 
Vladimir Karputov:

Bei diesem Beispiel friert der Computer ein. Was ich getan habe: Nachdem ich den Indikator im Editor auf das Diagramm gesetzt hatte, spielte ich mit verschiedenen Kombinationen von kommentierenden/dekommentierenden Zeilen 87 und 88 (wenn eine auf einmal, wenn zusammen)

Wiederhole die Leistung zweimal. Ich habe die Reihenfolge der Aktionen nicht aufgezeichnet. Das dritte Mal ist trivial, ich habe Angst, es zu überprüfen.


Hinzugefügt: Build 1607 x64


Ich habe den Rekord heute wiederholt - der Computer hängt sich auf, der RAM-Verbrauch ist von 2 GB auf 5,5 GB gestiegen. Es scheint gelungen zu sein, den Zeitplan zu schließen, aber der Computer hängt nun schon seit fünf Minuten.

Diesmal habe ich die Größe des Arrays begrenzt - nicht mehr als 300 Elemente. Wie Sie sehen können, hat es nicht geholfen 🤔.

 

Was sagt der Debugger?

 
o_o:

Was sagt der Debugger?


Jbug hat es nicht verstanden, ich habe Folgendes getan: einen Indikator verschoben und ein oder zwei Zeilen auskommentiert/kommentiert und kompiliert. Am Ende habe ich von einem Tablet aus geschrieben, der Laptop ist ausgefallen...
 
Vladimir Karputov:

Der Gbug hat es nicht geschafft, ich habe Folgendes getan: Ich bin mit dem Mauszeiger über den Indikator gefahren und habe ein oder zwei Zeilen auskommentiert und kompiliert. Am Ende habe ich von einem Tablet aus geschrieben, der Laptop ist ausgefallen...


Also, Laptop nach hartem Neustart kam wieder zum Leben, aber ich will nicht weiter zerstörerische Experiment durchzuführen - es gibt mehrere Expert Advisors auf dem Laptop laufen, so gibt es keine Lust, ein Einfrieren für eine ganze Stunde zu fangen.