CGraphicのテスト - 質問と提案 - ページ 6

 
Roman Konopelko:
CGraphicのクラスでは、ご質問の通り、全ての箇所でカラータイプをuintに置き換えています。

また、CCanvas クラスに新しいメソッドを追加し、プリミティブを指定した厚さで描画できるようにしました。
CCanvasの革新性に合わせて、CCurveのプロパティを拡張しました。
曲線を線で描くとき、線の太さと両端のスタイルを指定できるようになりました。


それはもう、すごいですよ。
 

スプライン(Bézier曲線による補間)の取り扱いを見直しました。その実装は、CGraphics クラスから直接 CCanvas に移され、Graphics ライブラリの外でスプラインを構築できるようになりました。

また、閉じたスプラインを描画するアルゴリズムが追加されました。

その結果、CCanvas クラスは、新たに 2 つのパブリック メソッドを持つようになりました。

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法によるBézier曲線による楕円の近似例。

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

結果

 

グラフィックス・ライブラリの多機能化に向けたもう一つの可能性として、カスタム曲線描画モードCURVE_CUSTOMがある。

このモードでは、ライブラリの標準ツールで可能なものとは異なる曲線を描くために、CGraphicクラスを継承し、...Plotメソッドをオーバーロードする必要がなくなります。

このモードを実装するために、CCurve クラスに新しいプロパティ CURVE_CUSTOM が追加される予定です。

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

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関数で描画することを指定しながら、そこに1本の曲線を追加します。また、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
 

ロマン・コノペルコ

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:

ロマン・コノペルコ

CGraphic::SetDefaultParameters関数に小さな誤りがあります。

色は不透明度を考慮して初期化する必要があります。

この点についてはすでに修正済みですが、残念ながらこの編集はまだビルドに反映されていません。
 

この例では、パソコンがフリーズしてしまいました。やったこと:エディタでチャートにインジケータを 置いた後、87行目と88行目のコメントアウト/アンコメントの組み合わせを変えて遊んでみた(1つずつの場合、一緒にした場合)。

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

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

2回達成を繰り返した。一連の動作は記録していない。3回目は些細なことですが......確認するのが怖いです。


追加:ビルド1607 x64

ファイル:
tst.mq5  8 kb
 
Vladimir Karputov:

この例では、パソコンがフリーズしてしまいました。やったこと:エディタでチャートにインジケータを 置いた後、87行目と88行目のコメントアウト/アンコメントの組み合わせを変えて遊んでみた(1つずつの場合、一緒にした場合)。

2回達成を繰り返した。一連の動作は記録していない。3回目は些細なことですが......確認するのが怖いです。


追加:ビルド1607 x64


今日も記録を繰り返しました。死んだコンピュータがハングアップし、RAMの消費量が2GBから5.5GBに上昇するのを確認することが出来ました。スケジュールを閉じることができたようだが、コンピュータが5分間もハングアップしている。

今回は配列のサイズに制限を設け、300要素以内としました。見ての通り役に立たなかった🤔。

 

デバッグは何を言っているのですか?

 
o_o:

デバッグは何を言っているのですか?


Jbugではダメだったので、インジケータにカーソルを合わせて1~2行をアンコメント/コメントし、コンパイルする、という方法をとりました。結局、タブレットから書くことになった。ノートパソコンが故障してしまったので...。
 
Vladimir Karputov:

gbugではダメで、こうしました。インジケータにカーソルを合わせて、1~2行コメントアウト/コメントアウトしてコンパイルしました。結局、タブレットから書くことになった。ノートパソコンが故障してしまったので...。


ラップトップはハード・リブートしたら復活したが、これ以上破壊的な実験はしたくない。ラップトップにはいくつかのExpert Advisorが走っているので、丸一時間フリーズを捕まえる気もないのだ。

理由: