DoEasyライブラリのグラフィックス(第75部): 基本的なグラフィック要素でプリミティブとテキストを処理するメソッド
内容
概念
より複雑なライブラリグラフィカルオブジェクトを作成するための基礎として使用される基本グラフィック要素オブジェクトクラスの開発を続けます。前回の記事では、基本グラフィカルオブジェクトを構築するという概念を生み出し、グラフィック要素を作成し、設定、変更、および受信できる基本的なプロパティを追加しました。
CCanvasクラスは「キャンバス上」に描画することを目的としており、グラフィカルプリミティブとテキストを操作するためのメソッドを備えています。現在の記事では、描画のためにCCanvasクラスメソッドにアクセスして処理できるようにする要素オブジェクトメソッドを作成します。これらのメソッドは単純で、要素オブジェクトクラスの子孫オブジェクトで詳細な描画メソッドを作成するために使用されます。
プリミティブを操作するためのメソッドを作成することに加えて、ファイルを操作するためのメソッドを作成します。グラフィカルオブジェクトはカスタムプログラムのGUI要素であり、別の時間枠に切り替えるときなどに、チャート上のプロパティ、ステータス、場所を「記憶」する必要があります。これを実現するために、オブジェクトのプロパティをファイルに保存します。オブジェクトの作成時には、このプロパティがオブジェクトから読み取られます。
ただし、ファイルはグラフィカルオブジェクトコレクションクラスで処理する必要がありますがこれはまだ開発していないため、グラフィカルオブジェクトのプロパティを保存およびアップロードするためのメソッドを追加するだけにします。グラフィカルオブジェクトコレクションクラスを作成する際に、ここで開発するグラフィック要素オブジェクトのプロパティを保存およびアップロードするためのメソッドを使用します。
また、色を扱うためのクラスが必要になります。ライブラリにも追加します。
このクラスは、Dmitry Fedoseevさんによって開発されてコミュニティに提出された、MQL5.comコードライブラリからとられます。
最終的な結果は、ライブラリのグラフィカルオブジェクトの基礎としてさらに使用できるグラフィック要素になります。
ライブラリクラスの改善
透明度のあるCanvasクラスオブジェクトをクリアする必要がある場合は、デフォルトでゼロを受け取るErase()メソッドを使用します。
//--- clear/fill color void Erase(const uint clr=0);
ここでの場合、この解決策は正しくありません。ゼロを使用してキャンバスをクリアするとアルファチャネル(色の透明度チャネル)を見失います。この方法でアルファチャネルをクリアしてキャンバスに描画すると、最終的にアーティファクトが発生します。
アルファチャンネルでキャンバスをクリアするには、ゼロの代わりに0x00FFFFFFを使用します。
これは、ARGB形式の完全に透明な黒色です(アルファ= 0、赤= 255、緑= 255、青= 255)。
\MQL5\Include\DoEasy\Defines.mqhで、そのような色を指定するためのマクロ置換を追加します。
//--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel //+------------------------------------------------------------------+
TextOut()メソッドを使用してキャンバスにテキストを表示する場合、テキストメッセージのアンカー角度(テキストの配置ポイント)を設定できます。これは、長方形の境界で、メッセージはこれに対して配置されます。アンカーポイントは、6つのフラグ(2つのフラグの組み合わせ)を使用して設定されます。これらはすべて以下にリストされています。
水平方向のテキスト配置フラグ:
- TA_LEFT - 外接する四角形の左側にあるアンカーポイント
- TA_CENTER - 外接する四角形の中央にある水平アンカーポイント
- TA_RIGHT - 外接する四角形の右側にあるアンカーポイント
垂直方向のテキスト配置フラグ:
- TA_TOP - 外接する四角形の上側にあるアンカーポイント
- TA_VCENTER - 外接する四角形の中央にある垂直アンカーポイント
- TA_BOTTOM - 外接する四角形の下側にあるアンカーポイント
フラグとそれらによって設定されるアンカー方法の可能な組み合わせを以下に示します。
どのフラグが最初に来るかについての混乱を避けるために、アンカーポイントを基準にしてテキストを整列させるためのすべての可能なフラグの組み合わせを指定するカスタム列挙を設定するだけです。
//+------------------------------------------------------------------+ //| Data for handling graphical elements | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of anchoring methods | //| (horizontal and vertical text alignment) | //+------------------------------------------------------------------+ enum ENUM_TEXT_ANCHOR { TEXT_ANCHOR_LEFT_TOP = 0, // Text anchor point at the upper left corner of the bounding rectangle TEXT_ANCHOR_CENTER_TOP = 1, // Text anchor point at the top center side of the bounding rectangle TEXT_ANCHOR_RIGHT_TOP = 2, // Text anchor point at the upper right corner of the bounding rectangle TEXT_ANCHOR_LEFT_CENTER = 4, // Text anchor point at the left center side of the bounding rectangle TEXT_ANCHOR_CENTER = 5, // Text anchor point at the center of the bounding rectangle TEXT_ANCHOR_RIGHT_CENTER = 6, // Text anchor point at the right center side of the bounding rectangle TEXT_ANCHOR_LEFT_BOTTOM = 8, // Text anchor point at the bottom left corner of the bounding rectangle TEXT_ANCHOR_CENTER_BOTTOM = 9, // Text anchor point at the bottom center side of the bounding rectangle TEXT_ANCHOR_RIGHT_BOTTOM = 10, // Text anchor point at the bottom right corner of the bounding rectangle }; //+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+
ここでは、テキストアンカーレベルごとに3つのフラグを設定します。
- 上部垂直アンカーポイント(TA_TOP) — 0:
- 左水平アンカーポイント(TA_LEFT) — 0
- 中央水平アンカーポイント(TA_CENTER) — 1
- 右水平アンカーポイント(TA_RIGHT) — 2
- 左水平アンカーポイント(TA_LEFT) — 0
- 中央垂直アンカーポイント(TA_VCENTER) — 4:
- 左水平アンカーポイント(TA_LEFT) — 0
- 中央水平アンカーポイント(TA_CENTER) — 1
- 右水平アンカーポイント(TA_RIGHT) — 2
- 左水平アンカーポイント(TA_LEFT) — 0
- 下部垂直アンカーポイント(TA_BOTTOM) — 8:
- 左水平アンカーポイント(TA_LEFT) — 0
- 中央水平アンカーポイント(TA_CENTER) — 1
- 右水平アンカーポイント(TA_RIGHT) — 2
- 左水平アンカーポイント(TA_LEFT) — 0
それぞれのENUM_TEXT_ANCHOR列挙値は、上記の正しく設定されたフラグの組み合わせに対応しています。
- TEXT_ANCHOR_LEFT_TOP = (TA_LEFT | TA_TOP) = 0,
- TEXT_ANCHOR_CENTER_TOP = (TA_CENTER | TA_TOP) = 1,
- TEXT_ANCHOR_RIGHT_TOP = (TA_RIGHT | TA_TOP) = 2,
- TEXT_ANCHOR_LEFT_CENTER = (TA_LEFT | TA_VCENTER) = 4,
- TEXT_ANCHOR_CENTER = (TA_CENTER | TA_VCENTER) = 5,
- TEXT_ANCHOR_RIGHT_CENTER = (TA_RIGHT | TA_VCENTER) = 6,
- TEXT_ANCHOR_LEFT_BOTTOM = (TA_LEFT | TA_BOTTOM) = 8,
- TEXT_ANCHOR_CENTER_BOTTOM = (TA_CENTER | TA_BOTTOM) = 9,
- TEXT_ANCHOR_RIGHT_BOTTOM = (TA_RIGHT | TA_BOTTOM) = 10.
この列挙をさらに使用して、アンカーポイントを基準にしたテキストの配置を指定します。
今はライブラリのグラフィカルな部分を開発しているため、色を操作するためのさまざまな方法が必要になります。
MQL5.comソースコードのライブラリには、Dmitry Fedoseevさんが公開してくれた色を操作するための優れた関数ライブラリがあります。
CColorsクラスを少し改善します。クラスオブジェクトを設定するために静的にするのではなく、コンテキスト解決演算子(::)を使用してそのメソッドに直接アクセスします。例:
class_name::variable
したがって、ライブラリに含まれているCColorsクラスを使用すると、コード内の任意の場所(カスタムプログラムを含む)でクラスメソッドにアクセスして、例えば不透明度128の青と不透明度64の赤の2つの色を混合できます。
CColors::BlendColors(ColorToARGB(clrBlue,128),ColorToARGB(clrRed,64));
クラスファイルをライブラリディレクトリ\MQL5\Include\DoEasy\Services\のColors.mqhに保存します。
//+------------------------------------------------------------------+ //| Colors.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com/ja/users/integer" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Class for working with color | //+------------------------------------------------------------------+ class CColors { private: static double Arctan2(const double x,const double y); static double Hue_To_RGB(double v1,double v2,double vH); public: //+--------------------------------------------------------------------+ //| The list of functions from http://www.easyrgb.com/index.php?X=MATH | //+--------------------------------------------------------------------+ static void RGBtoXYZ(const double aR,const double aG,const double aB,double &oX,double &oY,double &oZ); static void XYZtoRGB(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB); static void XYZtoYxy(const double aX,const double aY,const double aZ,double &oY,double &ox,double &oy); static void YxyToXYZ(const double aY,const double ax,const double ay,double &oX,double &oY,double &oZ); static void XYZtoHunterLab(const double aX,const double aY,const double aZ,double &oL,double &oa,double &ob); static void HunterLabToXYZ(const double aL,const double aa,const double ab,double &oX,double &oY,double &oZ); static void XYZtoCIELab(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEa,double &oCIEb); static void CIELabToXYZ(const double aCIEL,const double aCIEa,const double aCIEb,double &oX,double &oY,double &oZ); static void CIELabToCIELCH(const double aCIEL,const double aCIEa,const double aCIEb,double &oCIEL,double &oCIEC,double &oCIEH); static void CIELCHtoCIELab(const double aCIEL,const double aCIEC,const double aCIEH,double &oCIEL,double &oCIEa,double &oCIEb); static void XYZtoCIELuv(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEu,double &oCIEv); static void CIELuvToXYZ(const double aCIEL,const double aCIEu,const double aCIEv,double &oX,double &oY,double &oZ); static void RGBtoHSL(const double aR,const double aG,const double aB,double &oH,double &oS,double &oL); static void HSLtoRGB(const double aH,const double aS,const double aL,double &oR,double &oG,double &oB); static void RGBtoHSV(const double aR,const double aG,const double aB,double &oH,double &oS,double &oV); static void HSVtoRGB(const double aH,const double aS,const double aV,double &oR,double &oG,double &oB); static void RGBtoCMY(const double aR,const double aG,const double aB,double &oC,double &oM,double &oY); static void CMYtoRGB(const double aC,const double aM,const double aY,double &oR,double &oG,double &oB); static void CMYtoCMYK(const double aC,const double aM,const double aY,double &oC,double &oM,double &oY,double &oK); static void CMYKtoCMY(const double aC,const double aM,const double aY,const double aK,double &oC,double &oM,double &oY); static void RGBtoLab(const double aR,const double aG,const double aB,double &oL,double &oa,double &ob); //+------------------------------------------------------------------+ //| Other functions for working with color | //+------------------------------------------------------------------+ static void ColorToRGB(const color aColor,double &aR,double &aG,double &aB); static double GetR(const color aColor); static double GetG(const color aColor); static double GetB(const color aColor); static double GetA(const color aColor); static color RGBToColor(const double aR,const double aG,const double aB); static color MixColors(const color aCol1,const color aCol2,const double aK); static color BlendColors(const uint lower_color,const uint upper_color); static void Gradient(color &aColors[],color &aOut[],int aOutCount,bool aCycle=false); static void RGBtoXYZsimple(double aR,double aG,double aB,double &oX,double &oY,double &oZ); static void XYZtoRGBsimple(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB); static color Negative(const color aColor); static color StandardColor(const color aColor,int &aIndex); static double RGBtoGray(double aR,double aG,double aB); static double RGBtoGraySimple(double aR,double aG,double aB); }; //+------------------------------------------------------------------+ //| Class methods | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Arctan2 | //+------------------------------------------------------------------+ double CColors::Arctan2(const double x,const double y) { if(y==0) return(x<0 ?M_PI : 0); else { if(x>0) return(::atan(y/x)); if(x<0) return(y>0 ?atan(y/x)+M_PI : atan(y/x)-M_PI); else return(y<0 ?-M_PI_2 : M_PI_2); } } //+------------------------------------------------------------------+ //| Hue_To_RGB | //+------------------------------------------------------------------+ double CColors::Hue_To_RGB(double v1,double v2,double vH) { if(vH<0) vH+=1.0; if(vH>1.0) vH-=1; if((6.0*vH)<1.0) return(v1+(v2-v1)*6.0*vH); if((2.0*vH)<1.0) return(v2); if((3.0*vH)<2.0) return(v1+(v2-v1)*((2.0/3.0)-vH)*6.0); //--- return(v1); } //+------------------------------------------------------------------+ //| Conversion of RGB into XYZ | //+------------------------------------------------------------------+ void CColors::RGBtoXYZ(const double aR,const double aG,const double aB,double &oX,double &oY,double &oZ) { double var_R=aR/255; double var_G=aG/255; double var_B=aB/255; //--- if(var_R>0.04045) var_R=::pow((var_R+0.055)/1.055,2.4); else var_R=var_R/12.92; //--- if(var_G>0.04045) var_G=::pow((var_G+0.055)/1.055,2.4); else var_G=var_G/12.92; //--- if(var_B>0.04045) var_B=::pow((var_B+0.055)/1.055,2.4); else var_B=var_B/12.92; //--- var_R =var_R*100.0; var_G =var_G*100.0; var_B =var_B*100.0; oX =var_R*0.4124+var_G*0.3576+var_B*0.1805; oY =var_R*0.2126+var_G*0.7152+var_B*0.0722; oZ =var_R*0.0193+var_G*0.1192+var_B*0.9505; } //+------------------------------------------------------------------+ //| Conversion of XYZ into RGB | //+------------------------------------------------------------------+ void CColors::XYZtoRGB(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB) { double var_X =aX/100; double var_Y =aY/100; double var_Z =aZ/100; double var_R =var_X*3.2406+var_Y*-1.5372+var_Z*-0.4986; double var_G =var_X*(-0.9689)+var_Y*1.8758+var_Z*0.0415; double var_B =var_X*0.0557+var_Y*(-0.2040)+var_Z*1.0570; //--- if(var_R>0.0031308) var_R=1.055*(::pow(var_R,1.0/2.4))-0.055; else var_R=12.92*var_R; //--- if(var_G>0.0031308) var_G=1.055*(::pow(var_G,1.0/2.4))-0.055; else var_G=12.92*var_G; //--- if(var_B>0.0031308) var_B=1.055*(::pow(var_B,1.0/2.4))-0.055; else var_B=12.92*var_B; //--- oR =var_R*255.0; oG =var_G*255.0; oB =var_B*255.0; } //+------------------------------------------------------------------+ //| Conversion of XYZ into Yxy | //+------------------------------------------------------------------+ void CColors::XYZtoYxy(const double aX,const double aY,const double aZ,double &oY,double &ox,double &oy) { oY =aY; ox =aX/(aX+aY+aZ); oy =aY/(aX+aY+aZ); } //+------------------------------------------------------------------+ //| Conversion of Yxy into XYZ | //+------------------------------------------------------------------+ void CColors::YxyToXYZ(const double aY,const double ax,const double ay,double &oX,double &oY,double &oZ) { oX =ax*(aY/ay); oY =aY; oZ =(1.0-ax-ay)*(aY/ay); } //+------------------------------------------------------------------+ //| Conversion of XYZ into HunterLab | //+------------------------------------------------------------------+ void CColors::XYZtoHunterLab(const double aX,const double aY,const double aZ,double &oL,double &oa,double &ob) { oL =10.0*::sqrt(aY); oa =17.5*(((1.02*aX)-aY)/::sqrt(aY)); ob =7.0*((aY-(0.847*aZ))/::sqrt(aY)); } //+------------------------------------------------------------------+ //| Conversion of HunterLab into XYZ | //+------------------------------------------------------------------+ void CColors::HunterLabToXYZ(const double aL,const double aa,const double ab,double &oX,double &oY,double &oZ) { double var_Y =aL/10.0; double var_X =aa/17.5*aL/10.0; double var_Z =ab/7.0*aL/10.0; //--- oY =::pow(var_Y,2); oX =(var_X+oY)/1.02; oZ =-(var_Z-oY)/0.847; } //+------------------------------------------------------------------+ //| Conversion of XYZ into CIELab | //+------------------------------------------------------------------+ void CColors::XYZtoCIELab(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEa,double &oCIEb) { double ref_X =95.047; double ref_Y =100.0; double ref_Z =108.883; double var_X =aX/ref_X; double var_Y =aY/ref_Y; double var_Z =aZ/ref_Z; //--- if(var_X>0.008856) var_X=::pow(var_X,1.0/3.0); else var_X=(7.787*var_X)+(16.0/116.0); //--- if(var_Y>0.008856) var_Y=::pow(var_Y,1.0/3.0); else var_Y=(7.787*var_Y)+(16.0/116.0); //--- if(var_Z>0.008856) var_Z=::pow(var_Z,1.0/3.0); else var_Z=(7.787*var_Z)+(16.0/116.0); //--- oCIEL =(116.0*var_Y)-16.0; oCIEa =500.0*(var_X-var_Y); oCIEb =200*(var_Y-var_Z); } //+------------------------------------------------------------------+ //| Conversion of CIELab into ToXYZ | //+------------------------------------------------------------------+ void CColors::CIELabToXYZ(const double aCIEL,const double aCIEa,const double aCIEb,double &oX,double &oY,double &oZ) { double var_Y =(aCIEL+16.0)/116.0; double var_X =aCIEa/500.0+var_Y; double var_Z =var_Y-aCIEb/200.0; //--- if(::pow(var_Y,3)>0.008856) var_Y=::pow(var_Y,3); else var_Y=(var_Y-16.0/116.0)/7.787; //--- if(::pow(var_X,3)>0.008856) var_X=::pow(var_X,3); else var_X=(var_X-16.0/116.0)/7.787; //--- if(::pow(var_Z,3)>0.008856) var_Z=::pow(var_Z,3); else var_Z=(var_Z-16.0/116.0)/7.787; //--- double ref_X =95.047; double ref_Y =100.0; double ref_Z =108.883; //--- oX =ref_X*var_X; oY =ref_Y*var_Y; oZ =ref_Z*var_Z; } //+------------------------------------------------------------------+ //| Conversion of CIELab into CIELCH | //+------------------------------------------------------------------+ void CColors::CIELabToCIELCH(const double aCIEL,const double aCIEa,const double aCIEb,double &oCIEL,double &oCIEC,double &oCIEH) { double var_H=Arctan2(aCIEb,aCIEa); //--- if(var_H>0) var_H=(var_H/M_PI)*180.0; else var_H=360.0-(::fabs(var_H)/M_PI)*180.0; //--- oCIEL =aCIEL; oCIEC =::sqrt(::pow(aCIEa,2)+::pow(aCIEb,2)); oCIEH =var_H; } //+------------------------------------------------------------------+ //| Conversion of CIELCH into CIELab | //+------------------------------------------------------------------+ void CColors::CIELCHtoCIELab(const double aCIEL,const double aCIEC,const double aCIEH,double &oCIEL,double &oCIEa,double &oCIEb) { //--- Arguments from 0 to 360° oCIEL =aCIEL; oCIEa =::cos(M_PI*aCIEH/180.0)*aCIEC; oCIEb =::sin(M_PI*aCIEH/180)*aCIEC; } //+------------------------------------------------------------------+ //| Conversion of XYZ into CIELuv | //+------------------------------------------------------------------+ void CColors::XYZtoCIELuv(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEu,double &oCIEv) { double var_U =(4.0*aX)/(aX+(15.0*aY)+(3.0*aZ)); double var_V =(9.0*aY)/(aX+(15.0*aY)+(3.0*aZ)); double var_Y =aY/100.0; //--- if(var_Y>0.008856) var_Y=::pow(var_Y,1.0/3.0); else var_Y=(7.787*var_Y)+(16.0/116.0); //--- double ref_X =95.047; double ref_Y =100.000; double ref_Z =108.883; double ref_U =(4.0*ref_X)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double ref_V =(9.0*ref_Y)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); //--- oCIEL =(116.0*var_Y)-16.0; oCIEu =13.0*oCIEL*(var_U-ref_U); oCIEv =13.0*oCIEL*(var_V-ref_V); } //+------------------------------------------------------------------+ //| Conversion of CIELuv into XYZ | //+------------------------------------------------------------------+ void CColors::CIELuvToXYZ(const double aCIEL,const double aCIEu,const double aCIEv,double &oX,double &oY,double &oZ) { double var_Y=(aCIEL+16.0)/116.0; //--- if(::pow(var_Y,3)>0.008856) var_Y=::pow(var_Y,3); else var_Y=(var_Y-16.0/116.0)/7.787; //--- double ref_X =95.047; double ref_Y =100.000; double ref_Z =108.883; double ref_U =(4.0*ref_X)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double ref_V =(9.0*ref_Y)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double var_U =aCIEu/(13.0*aCIEL)+ref_U; double var_V =aCIEv/(13.0*aCIEL)+ref_V; //--- oY=var_Y*100.0; oX=-(9.0*oY*var_U)/((var_U-4.0)*var_V-var_U*var_V); oZ=(9.0*oY-(15.0*var_V*oY)-(var_V*oX))/(3.0*var_V); } //+------------------------------------------------------------------+ //| Conversion of RGB into HSL | //+------------------------------------------------------------------+ void CColors::RGBtoHSL(const double aR,const double aG,const double aB,double &oH,double &oS,double &oL) { double var_R =(aR/255); double var_G =(aG/255); double var_B =(aB/255); double var_Min =::fmin(var_R,::fmin(var_G,var_B)); double var_Max =::fmax(var_R,::fmax(var_G,var_B)); double del_Max =var_Max-var_Min; //--- oL=(var_Max+var_Min)/2; //--- if(del_Max==0) { oH=0; oS=0; } else { if(oL<0.5) oS=del_Max/(var_Max+var_Min); else oS=del_Max/(2.0-var_Max-var_Min); //--- double del_R =(((var_Max-var_R)/6.0)+(del_Max/2.0))/del_Max; double del_G =(((var_Max-var_G)/6.0)+(del_Max/2.0))/del_Max; double del_B =(((var_Max-var_B)/6.0)+(del_Max/2.0))/del_Max; //--- if(var_R==var_Max) oH=del_B-del_G; else if(var_G==var_Max) oH=(1.0/3.0)+del_R-del_B; else if(var_B==var_Max) oH=(2.0/3.0)+del_G-del_R; //--- if(oH<0) oH+=1.0; //--- if(oH>1) oH-=1.0; } } //+------------------------------------------------------------------+ //| Conversion of HSL into RGB | //+------------------------------------------------------------------+ void CColors::HSLtoRGB(const double aH,const double aS,const double aL,double &oR,double &oG,double &oB) { if(aS==0) { oR=aL*255; oG=aL*255; oB=aL*255; } else { double var_2=0.0; //--- if(aL<0.5) var_2=aL*(1.0+aS); else var_2=(aL+aS)-(aS*aL); //--- double var_1=2.0*aL-var_2; oR =255.0*Hue_To_RGB(var_1,var_2,aH+(1.0/3.0)); oG =255.0*Hue_To_RGB(var_1,var_2,aH); oB =255.0*Hue_To_RGB(var_1,var_2,aH-(1.0/3.0)); } } //+------------------------------------------------------------------+ //| Conversion of RGB into HSV | //+------------------------------------------------------------------+ void CColors::RGBtoHSV(const double aR,const double aG,const double aB,double &oH,double &oS,double &oV) { const double var_R =(aR/255.0); const double var_G =(aG/255.0); const double var_B =(aB/255.0); const double var_Min =::fmin(var_R,::fmin(var_G, var_B)); const double var_Max =::fmax(var_R,::fmax(var_G,var_B)); const double del_Max =var_Max-var_Min; //--- oV=var_Max; //--- if(del_Max==0) { oH=0; oS=0; } else { oS=del_Max/var_Max; const double del_R =(((var_Max-var_R)/6.0)+(del_Max/2))/del_Max; const double del_G =(((var_Max-var_G)/6.0)+(del_Max/2))/del_Max; const double del_B =(((var_Max-var_B)/6.0)+(del_Max/2))/del_Max; //--- if(var_R==var_Max) oH=del_B-del_G; else if(var_G==var_Max) oH=(1.0/3.0)+del_R-del_B; else if(var_B==var_Max) oH=(2.0/3.0)+del_G-del_R; //--- if(oH<0) oH+=1.0; //--- if(oH>1.0) oH-=1.0; } } //+------------------------------------------------------------------+ //| Conversion of HSV into RGB | //+------------------------------------------------------------------+ void CColors::HSVtoRGB(const double aH,const double aS,const double aV,double &oR,double &oG,double &oB) { if(aS==0) { oR =aV*255.0; oG =aV*255.0; oB =aV*255.0; } else { double var_h=aH*6.0; //--- if(var_h==6) var_h=0; //--- int var_i =int(var_h); double var_1 =aV*(1.0-aS); double var_2 =aV*(1.0-aS*(var_h-var_i)); double var_3 =aV*(1.0-aS*(1.0-(var_h-var_i))); double var_r =0.0; double var_g =0.0; double var_b =0.0; //--- if(var_i==0) { var_r =aV; var_g =var_3; var_b =var_1; } else if(var_i==1.0) { var_r=var_2; var_g=aV; var_b=var_1; } else if(var_i==2.0) { var_r=var_1; var_g=aV; var_b=var_3; } else if(var_i==3) { var_r=var_1; var_g=var_2; var_b=aV; } else if(var_i==4) { var_r=var_3; var_g=var_1; var_b=aV; } else { var_r=aV; var_g=var_1; var_b=var_2; } //--- oR =var_r*255.0; oG =var_g*255.0; oB =var_b*255.0; } } //+------------------------------------------------------------------+ //| Conversion of RGB into CMY | //+------------------------------------------------------------------+ void CColors::RGBtoCMY(const double aR,const double aG,const double aB,double &oC,double &oM,double &oY) { oC =1.0-(aR/255.0); oM =1.0-(aG/255.0); oY =1.0-(aB/255.0); } //+------------------------------------------------------------------+ //| Conversion of CMY into RGB | //+------------------------------------------------------------------+ void CColors::CMYtoRGB(const double aC,const double aM,const double aY,double &oR,double &oG,double &oB) { oR =(1.0-aC)*255.0; oG =(1.0-aM)*255.0; oB =(1.0-aY)*255.0; } //+------------------------------------------------------------------+ //| Conversion of CMY into CMYK | //+------------------------------------------------------------------+ void CColors::CMYtoCMYK(const double aC,const double aM,const double aY,double &oC,double &oM,double &oY,double &oK) { double var_K=1; //--- if(aC<var_K) var_K=aC; if(aM<var_K) var_K=aM; if(aY<var_K) var_K=aY; //--- if(var_K==1.0) { oC =0; oM =0; oY =0; } else { oC =(aC-var_K)/(1.0-var_K); oM =(aM-var_K)/(1.0-var_K); oY =(aY-var_K)/(1.0-var_K); } //--- oK=var_K; } //+------------------------------------------------------------------+ //| Conversion of CMYK into CMY | //+------------------------------------------------------------------+ void CColors::CMYKtoCMY(const double aC,const double aM,const double aY,const double aK,double &oC,double &oM,double &oY) { oC =(aC*(1.0-aK)+aK); oM =(aM*(1.0-aK)+aK); oY =(aY*(1.0-aK)+aK); } //+------------------------------------------------------------------+ //| Conversion of RGB into Lab | //+------------------------------------------------------------------+ void CColors::RGBtoLab(const double aR,const double aG,const double aB,double &oL,double &oa,double &ob) { double X=0,Y=0,Z=0; RGBtoXYZ(aR,aG,aB,X,Y,Z); XYZtoHunterLab(X,Y,Z,oL,oa,ob); } //+------------------------------------------------------------------+ //| Getting values of the RGB components | //+------------------------------------------------------------------+ void CColors::ColorToRGB(const color aColor,double &aR,double &aG,double &aB) { aR =GetR(aColor); aG =GetG(aColor); aB =GetB(aColor); } //+------------------------------------------------------------------+ //| Getting the R component value | //+------------------------------------------------------------------+ double CColors::GetR(const color aColor) { return(aColor&0xff); } //+------------------------------------------------------------------+ //| Getting the G component value | //+------------------------------------------------------------------+ double CColors::GetG(const color aColor) { return((aColor>>8)&0xff); } //+------------------------------------------------------------------+ //| Getting the B component value | //+------------------------------------------------------------------+ double CColors::GetB(const color aColor) { return((aColor>>16)&0xff); } //+------------------------------------------------------------------+ //| Getting the A component value | //+------------------------------------------------------------------+ double CColors::GetA(const color aColor) { return(double(uchar((aColor)>>24))); } //+------------------------------------------------------------------+ //| Conversion of RGB into const color | //+------------------------------------------------------------------+ color CColors::RGBToColor(const double aR,const double aG,const double aB) { int int_r =(int)::round(aR); int int_g =(int)::round(aG); int int_b =(int)::round(aB); int Color =0; //--- Color=int_b; Color<<=8; Color|=int_g; Color<<=8; Color|=int_r; //--- return((color)Color); } //+------------------------------------------------------------------+ //| Getting the value of the intermediary color between two colors | //+------------------------------------------------------------------+ color CColors::MixColors(const color aCol1,const color aCol2,const double aK) { //--- aK - from 0 to 1 double R1=0.0,G1=0.0,B1=0.0,R2=0.0,G2=0.0,B2=0.0; //--- ColorToRGB(aCol1,R1,G1,B1); ColorToRGB(aCol2,R2,G2,B2); //--- R1+=(int)::round(aK*(R2-R1)); G1+=(int)::round(aK*(G2-G1)); B1+=(int)::round(aK*(B2-B1)); //--- return(RGBToColor(R1,G1,B1)); } //+------------------------------------------------------------------+ //| Blending two colors considering the transparency of color on top | //+------------------------------------------------------------------+ color CColors::BlendColors(const uint lower_color,const uint upper_color) { double r1=0,g1=0,b1=0; double r2=0,g2=0,b2=0,alpha=0; double r3=0,g3=0,b3=0; //--- Convert the colors in ARGB format uint pixel_color=::ColorToARGB(upper_color); //--- Get the components of the lower and upper colors ColorToRGB(lower_color,r1,g1,b1); ColorToRGB(pixel_color,r2,g2,b2); //--- Get the transparency percentage from 0.00 to 1.00 alpha=GetA(upper_color)/255.0; //--- If there is transparency if(alpha<1.0) { //--- Blend the components taking the alpha channel into account r3=(r1*(1-alpha))+(r2*alpha); g3=(g1*(1-alpha))+(g2*alpha); b3=(b1*(1-alpha))+(b2*alpha); //--- Adjustment of the obtained values r3=(r3>255)?255 : r3; g3=(g3>255)?255 : g3; b3=(b3>255)?255 : b3; } else { r3=r2; g3=g2; b3=b2; } //--- Combine the obtained components and return the color return(RGBToColor(r3,g3,b3)); } //+------------------------------------------------------------------+ //| Getting an array of the specified size with a color gradient | //+------------------------------------------------------------------+ void CColors::Gradient(color &aColors[], // List of colors color &aOut[], // Return array int aOutCount, // Set the size of the return array bool aCycle=false) // Closed-loop cycle. Return array ends with the same color as it starts with { ::ArrayResize(aOut,aOutCount); //--- int InCount =::ArraySize(aColors)+aCycle; int PrevJ =0; int nci =0; double K =0.0; //--- for(int i=1; i<InCount; i++) { int J=(aOutCount-1)*i/(InCount-1); //--- for(int j=PrevJ; j<=J; j++) { if(aCycle && i==InCount-1) { nci =0; K =1.0*(j-PrevJ)/(J-PrevJ+1); } else { nci =i; K =1.0*(j-PrevJ)/(J-PrevJ); } aOut[j]=MixColors(aColors[i-1],aColors[nci],K); } PrevJ=J; } } //+------------------------------------------------------------------+ //| One more variant of conversion of RGB into XYZ and | //| corresponding conversion of XYZ into RGB | //+------------------------------------------------------------------+ void CColors::RGBtoXYZsimple(double aR,double aG,double aB,double &oX,double &oY,double &oZ) { aR/=255; aG/=255; aB/=255; aR*=100; aG*=100; aB*=100; //--- oX=0.431*aR+0.342*aG+0.178*aB; oY=0.222*aR+0.707*aG+0.071*aB; oZ=0.020*aR+0.130*aG+0.939*aB; } //+------------------------------------------------------------------+ //| XYZtoRGBsimple | //+------------------------------------------------------------------+ void CColors::XYZtoRGBsimple(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB) { oR=3.063*aX-1.393*aY-0.476*aZ; oG=-0.969*aX+1.876*aY+0.042*aZ; oB=0.068*aX-0.229*aY+1.069*aZ; } //+------------------------------------------------------------------+ //| Negative color | //+------------------------------------------------------------------+ color CColors::Negative(const color aColor) { double R=0.0,G=0.0,B=0.0; ColorToRGB(aColor,R,G,B); //--- return(RGBToColor(255-R,255-G,255-B)); } //+------------------------------------------------------------------+ //| Search for the most similar color | //| in the set of standard colors of the terminal | //+------------------------------------------------------------------+ color CColors::StandardColor(const color aColor,int &aIndex) { color m_c[]= { clrBlack,clrDarkGreen,clrDarkSlateGray,clrOlive,clrGreen,clrTeal,clrNavy,clrPurple,clrMaroon,clrIndigo, clrMidnightBlue,clrDarkBlue,clrDarkOliveGreen,clrSaddleBrown,clrForestGreen,clrOliveDrab,clrSeaGreen, clrDarkGoldenrod,clrDarkSlateBlue,clrSienna,clrMediumBlue,clrBrown,clrDarkTurquoise,clrDimGray, clrLightSeaGreen,clrDarkViolet,clrFireBrick,clrMediumVioletRed,clrMediumSeaGreen,clrChocolate,clrCrimson, clrSteelBlue,clrGoldenrod,clrMediumSpringGreen,clrLawnGreen,clrCadetBlue,clrDarkOrchid,clrYellowGreen, clrLimeGreen,clrOrangeRed,clrDarkOrange,clrOrange,clrGold,clrYellow,clrChartreuse,clrLime,clrSpringGreen, clrAqua,clrDeepSkyBlue,clrBlue,clrFuchsia,clrRed,clrGray,clrSlateGray,clrPeru,clrBlueViolet,clrLightSlateGray, clrDeepPink,clrMediumTurquoise,clrDodgerBlue,clrTurquoise,clrRoyalBlue,clrSlateBlue,clrDarkKhaki,clrIndianRed, clrMediumOrchid,clrGreenYellow,clrMediumAquamarine,clrDarkSeaGreen,clrTomato,clrRosyBrown,clrOrchid, clrMediumPurple,clrPaleVioletRed,clrCoral,clrCornflowerBlue,clrDarkGray,clrSandyBrown,clrMediumSlateBlue, clrTan,clrDarkSalmon,clrBurlyWood,clrHotPink,clrSalmon,clrViolet,clrLightCoral,clrSkyBlue,clrLightSalmon, clrPlum,clrKhaki,clrLightGreen,clrAquamarine,clrSilver,clrLightSkyBlue,clrLightSteelBlue,clrLightBlue, clrPaleGreen,clrThistle,clrPowderBlue,clrPaleGoldenrod,clrPaleTurquoise,clrLightGray,clrWheat,clrNavajoWhite, clrMoccasin,clrLightPink,clrGainsboro,clrPeachPuff,clrPink,clrBisque,clrLightGoldenrod,clrBlanchedAlmond, clrLemonChiffon,clrBeige,clrAntiqueWhite,clrPapayaWhip,clrCornsilk,clrLightYellow,clrLightCyan,clrLinen, clrLavender,clrMistyRose,clrOldLace,clrWhiteSmoke,clrSeashell,clrIvory,clrHoneydew,clrAliceBlue,clrLavenderBlush, clrMintCream,clrSnow,clrWhite,clrDarkCyan,clrDarkRed,clrDarkMagenta,clrAzure,clrGhostWhite,clrFloralWhite }; //--- double m_rv=0.0,m_gv=0.0,m_bv=0.0; //--- ColorToRGB(aColor,m_rv,m_gv,m_bv); //--- double m_md=0.3*::pow(255,2)+0.59*::pow(255,2)+0.11*::pow(255,2)+1; aIndex=0; //--- for(int i=0; i<138; i++) { double m_d=0.3*::pow(GetR(m_c[i])-m_rv,2)+0.59*::pow(GetG(m_c[i])-m_gv,2)+0.11*::pow(GetB(m_c[i])-m_bv,2); //--- if(m_d<m_md) { m_md =m_d; aIndex =i; } } //--- return(m_c[aIndex]); } //+------------------------------------------------------------------+ //| Conversion into gray color | //+------------------------------------------------------------------+ double CColors::RGBtoGray(double aR,double aG,double aB) { aR/=255; aG/=255; aB/=255; //--- aR=::pow(aR,2.2); aG=::pow(aG,2.2); aB=::pow(aB,2.2); //--- double rY=0.21*aR+0.72*aG+0.07*aB; rY=::pow(rY,1.0/2.2); //--- return(rY); } //+------------------------------------------------------------------+ //| Simple conversion into gray color | //+------------------------------------------------------------------+ double CColors::RGBtoGraySimple(double aR,double aG,double aB) { aR/=255; aG/=255; aB/=255; double rY=0.3*aR+0.59*aG+0.11*aB; //--- return(rY); } //+------------------------------------------------------------------+
加えられたすべての変更は、各メソッドへの「static」修飾子の設定と、コーディングスタイルに合った比企能的な変更(メソッド引数の変数名を除く)に要約されます。さらに、RGBカラーモデルをLabに変換するためのRGBtoLab()メソッドを追加しました。このメソッドは、RGBモデルをXYZに変換するだけで、XYZはLabカラーモデルに変換されます。この問題はかつて、Anatoli Kazharskiさんの記事「グラフィカルインタフェースIX:カラーピッカーコントロール(チャプター1)」で検討されました。
私は単に著者のアドバイスに従っただけです。
CColorsクラスをライブラリ全体とそれに基づくプログラムに表示するには、クラスファイルを\MQL5\Include\DoEasy\Services\DELib.mqhのライブラリサービス関数のファイルにインクルードします。
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ja/users/artmedia70" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" #include "Pause.mqh" #include "Colors.mqh" //+------------------------------------------------------------------+ //| Service functions | //+------------------------------------------------------------------+
ここではクラスは必要ありません。後でグラフィック要素オブジェクトの子孫クラスを作成するときに使用を開始します。
各グラフィカルオブジェクトは少なくともそのサイズとチャート上の位置の座標を備えています。さらに、オブジェクトには、プログラムの実行中に変更できる多くのプロパティがあります。ただし、プログラムを再起動したり時間枠を切り替えたりすると、プログラムの実行中にグラフィカルオブジェクトに加えられたすべての変更がリセットされます。各オブジェクトにそのプロパティの状態を記憶させるには、それらを外部に保存する必要があります。この場合、プログラムを再起動した後、操作中に作成および変更されたすべてのグラフィカルオブジェクトは、ファイルから適切なプロパティ(リセット時点で関連)を読み取り、復元します。これを実現するには、グラフィック要素オブジェクトクラスに2つのメソッドを追加する必要があります。1つはファイルにオブジェクトプロパティを書き込むためのメソッドで、もう1つはファイルからオブジェクトプロパティを読み取るためのメソッドです。
オブジェクトプロパティの読み取りと書き込みを行うために、オブジェクトプロパティを構造体に保存します。構造体はファイルに保存することも、StructToCharArray()とCharArrayToStruct()標準関数を使用してファイルから読み取ることもできます。
キャンバスに基づく各グラフィカルオブジェクトは、メソッドが設定されるグラフィック要素オブジェクトの子孫であるため、各グラフィカルオブジェクトは、プロパティをファイルに保存してファイルから読み取るためのメソッドを備えています。したがって、オブジェクトが複合である場合(つまり、グラフィック要素に基づいて他のオブジェクトで構成されている場合)、従属オブジェクトのリスト内のオブジェクトインデックスに従って、すべての従属オブジェクトの状態を1つずつ復元できます(インデックスは要素オブジェクトプロパティのENUM_CANV_ELEMENT_PROP_INTEGER列挙型のCANV_ELEMENT_PROP_NUM定数に格納されます)。
本稿では、プロパティをファイルに保存したり、ファイルから読み取ったりすることはしません。これは、グラフィカルオブジェクトコレクションクラスから行う必要があるためです。後ほどグラフィック要素を作成した後で検討します。とにかく、書き込みと読み取りのメソッドはここに追加されます。
グラフィック要素はすべてのCGBaseObjライブラリグラフィカルオブジェクトの基本オブジェクトの子孫であるため、まずオブジェクトプロパティから構造を作成するための仮想メソッドと復元するための仮想メソッドをオブジェクトクラスファイル(\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh)のprotectedセクションの構造体からのオブジェクトプロパティに設定しましょう。
protected: string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Chart ID int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift int m_type; // Object type //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} public:
これらのメソッドはここでは何もせず、クラスの子孫で再定義されます。クラスの最も近い子孫は、\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhにあるグラフィック要素オブジェクトのクラスです。protectedセクションで同じ仮想メソッドを宣言します。
//+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGCnvElement : public CGBaseObj { protected: CCanvas m_canvas; // CCanvas class object CPause m_pause; // Pause class object //--- Return the cursor position relative to the (1) entire element and (2) the element active area bool CursorInsideElement(const int x,const int y); bool CursorInsideActiveArea(const int x,const int y); //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void); virtual void StructToObject(void); private:
privateセクションで、すべてのオブジェクトプロパティを格納するための構造体、構造体型のオブジェクト、オブジェクト構造配列を宣言します。
private: struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type int number; // Element index in the list long chart_id; // Chart ID int subwindow; // Chart subwindow index int coord_x; // Form's X coordinate on the chart int coord_y; // Form's Y coordinate on the chart int width; // Element width int height; // Element height int edge_right; // Element right border int edge_bottom; // Element bottom border int act_shift_left; // Active area offset from the left edge of the element int act_shift_top; // Active area offset from the top edge of the element int act_shift_right; // Active area offset from the right edge of the element int act_shift_bottom; // Active area offset from the bottom edge of the element uchar opacity; // Element opacity color color_bg; // Element background color bool movable; // Element moveability flag bool active; // Element activity flag int coord_act_x; // X coordinate of the element active area int coord_act_y; // Y coordinate of the element active area int coord_act_right; // Right border of the element active area int coord_act_bottom; // Bottom border of the element active area //--- Object real properties //--- Object string properties uchar name_obj[64]; // Graphical element object name uchar name_res[64]; // Graphical resource name }; SData m_struct_obj; // Object structure uchar m_uchar_array[]; // uchar array of the object structure long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties
クラスのpublicセクションで、オブジェクトプロパティをファイルに書き込むメソッドとファイルから読み取るメソッドを宣言します。
public: //--- Set object (1) integer, (2) real and (3) string properties void SetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)];} string GetProperty(ENUM_CANV_ELEMENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)];} //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return false;} virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Compare CGCnvElement objects with each other by all possible properties (for sorting the lists by a specified object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CGCnvElement objects with each other by all properties (to search equal objects) bool IsEqual(CGCnvElement* compared_obj) const; //--- (1) Save the object to file and (2) upload the object from the file virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); //--- Create the element
オブジェクトには実数プロパティがないため、オブジェクトによって実数プロパティをサポートするフラグを返す仮想メソッドはfalseを返す必要があります。
宣言されたメソッドをクラス本体の外側に実装します。
以下は、プロパティからオブジェクト構造体を作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the object structure | //+------------------------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Save integer properties this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Element ID this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Graphical element type this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM); // Eleemnt ID in the list this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); // Chart ID this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); // Chart subwindow index this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); // Form's X coordinate on the chart this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); // Form's Y coordinate on the chart this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); // Element width this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); // Element height this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT); // Element right edge this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM); // Element bottom edge this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); // Active area offset from the left edge of the element this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); // Active area offset from the top edge of the element this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); // Active area offset from the right edge of the element this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element this.m_struct_obj.opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY); // Element opacity this.m_struct_obj.color_bg=(color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG); // Element background color this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); // Element moveability flag this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); // Element activity flag this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X); // X coordinate of the element active area this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y); // Y coordinate of the element active area this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT); // Right border of the element active area this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM); // Bottom border of the element active area //--- Save real properties //--- Save string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);// Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);// Graphical resource name //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY),(string)::GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
ここではすべて簡単です。構造体の各整数フィールドは適切なオブジェクトプロパティを受け取り、オブジェクト文字列プロパティは構造体の適切なuchar配列に保存されます。次に、StructToCharArray()を使用して、新しく作成したオブジェクトプロパティ構造をuchar配列に保存します。
(構造体を配列に保存できなかった場合は、エラーを通知してfalseを返します。)その結果、trueを返します。
以下は、構造体からオブジェクトのプロパティを復元するメソッドです。
//+------------------------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x); // Form's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y); // Form's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height); // Element height this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right); // Element right edge this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom); // Element bottom edge this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_OPACITY,this.m_struct_obj.opacity); // Element opacity this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,this.m_struct_obj.color_bg); // Element background color this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom); // Bottom border of the element active area //--- Save real properties //--- Save string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));// Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));// Graphical resource name } //+------------------------------------------------------------------+
ここで、各整数オブジェクトプロパティは適切な構造体フィールドから値を受け取り、適切なuchar配列構造の内容は、CharArrayToString()を使用してオブジェクト文字列プロパティに読み込まれます。
以下は、オブジェクトをファイルに保存するメソッドです。
//+------------------------------------------------------------------+ //| Save the object to the file | //+------------------------------------------------------------------+ bool CGCnvElement::Save(const int file_handle) { if(!this.ObjectToStruct()) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT)); return false; } if(::FileWriteArray(file_handle,this.m_uchar_array)==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_WRITE_UARRAY_TO_FILE)); return false; } return true; } //+------------------------------------------------------------------+
このメソッドは、オブジェクトプロパティの保存先であるファイルのハンドルを受け取ります。次に、オブジェクトのプロパティは、上記で検討したObjectToStruct()メソッドを使用して構造体に保存されます。構造体の構築時に作成されたuchar配列は、FileWriteArray()を使用してファイルに書き込まれ、trueが返されます。失敗した場合、メソッドは操作ログにエラーメッセージを表示し、falseを返します。
以下は、ファイルからオブジェクトのプロパティをアップロードするメソッドです。
//+------------------------------------------------------------------+ //| Upload the object from the file | //+------------------------------------------------------------------+ bool CGCnvElement::Load(const int file_handle) { if(::FileReadArray(file_handle,this.m_uchar_array)==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_LOAD_UARRAY_FROM_FILE)); return false; } if(!::CharArrayToStruct(this.m_struct_obj,this.m_uchar_array)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT_FROM_UARRAY)); return false; } this.StructToObject(); return true; } //+------------------------------------------------------------------+
このメソッドは、オブジェクトプロパティが保存されているファイルのハンドルを受け取ります。次に、ファイルのオブジェクトプロパティが、FileReadArray()を使用してuchar配列にアップロードされます。アップロードされたプロパティは、CharArrayToStruct()を使用して構造体にコピーされます。ファイルから入力された構造体は、上記のStructToObject()メソッドを使用してオブジェクトプロパティに設定され、trueが返されます。ファイルからの読み取りまたは取得した配列の構造体へのコピーがエラーで終了した場合、メソッドはそのことを通知し、falseを返します。
オブジェクトプロパティへの簡略化されたアクセスのためのメソッドのブロックは、要素の右のエッジと下のエッジを返すメソッド、要素の背景色を設定するメソッドと返すメソッドを受け取ります。また、要素IDとそのコンポジットオブジェクト内の要素リストでのインデックスを返すメソッドを受け取ります。
//+------------------------------------------------------------------+ //| The methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge, bool SetCoordX(const int coord_x); bool SetCoordY(const int coord_y); bool SetWidth(const int width); bool SetHeight(const int height); void SetRightEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); } void SetBottomEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); } //--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element, //--- (5) all shifts of the active area edges relative to the element, (6) the element background color and (7) the element opacity void SetActiveAreaLeftShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value)); } void SetActiveAreaRightShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value)); } void SetActiveAreaTopShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value)); } void SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value)); } void SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift); void SetColorBG(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour); } void SetOpacity(const uchar value,const bool redraw=false); //--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeftShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); } int ActiveAreaRightShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); } int ActiveAreaTopShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); } int ActiveAreaBottomShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM); } //--- Return the coordinate (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeft(void) const { return int(this.CoordX()+this.ActiveAreaLeftShift()); } int ActiveAreaRight(void) const { return int(this.RightEdge()-this.ActiveAreaRightShift()); } int ActiveAreaTop(void) const { return int(this.CoordY()+this.ActiveAreaTopShift()); } int ActiveAreaBottom(void) const { return int(this.BottomEdge()-this.ActiveAreaBottomShift()); } //--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge color ColorBG(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG); } uchar Opacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY); } int RightEdge(void) const { return this.CoordX()+this.m_canvas.Width(); } int BottomEdge(void) const { return this.CoordY()+this.m_canvas.Height(); } //--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height, int CoordX(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); } int CoordY(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); } int Width(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); } int Height(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); } //--- Return the element (1) moveability and (2) activity flag bool Movable(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); } bool Active(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); } //--- Return (1) the object name, (2) the graphical resource name, (3) the chart ID and (4) the chart subwindow index string NameObj(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ); } string NameRes(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_RES); } long ChartID(void) const { return this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); } int WindowNum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); } //--- Return (1) the element ID and (2) index in the list int ID(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID); } int Number(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM); } //+------------------------------------------------------------------+
これらのメソッドはすべて、適切な要素オブジェクトプロパティを返すだけです。
プリミティブ操作メソッド
CCanvasクラスは、キャンバス上にさまざまなグラフィカルプリミティブを描画するための十分な機会を提供します。各ピクセルの色を読み取るか、必要な色と透明度を設定することができます。このクラスは、単に色をピクセルに設定するだけでなく、ピクセルごとに(平滑化なしで)、またはさまざまな平滑化方法を使用して、さまざまな図形を描画するためのツールを提供します。
グラフィック要素オブジェクトクラスは、ユーザーにCCanvasクラスの描画メソッドへのアクセスを提供するためのものです。私たちのメソッドは単に、CCanvasクラスメソッドの呼び出しをわずかに単純化するものです。単純化したのは、色が通常の方法で設定されるようにしたことです。必要な色をcolor形式で指定し、色の不透明度(0 —透明、255 —完全に不透明)を設定します。 CCanvasクラスのメソッドは、uintARGB形式ですぐに色を指定するように「要求」しますが、これは単なる数値です。誰もがこの形式で目的の色を指定することに慣れているわけではありません(半透明の灰色:0x7F7F7F7F)グラフィック要素オブジェクトから継承される後続のクラスでは、作成された各クラスに固有の便利な機能を追加することで、描画機能の範囲を拡大します。残りのグラフィカルオブジェクトを作成するための基本である同じクラスでは、描画メソッドは単純でわかりやすい必要があります。
オブジェクトプロパティへのアクセスを簡素化するためのメソッドのブロックの後に、新しいコードブロックが続きます。目的に応じて配置してみました。
データ受信メソッドは「Get」で始まり、データ設定メソッドは「Set」で始まります。
以下は、指定された座標を持つ点の色を受け取るメソッドです。
//+------------------------------------------------------------------+ //| The methods of receiving raster data | //+------------------------------------------------------------------+ //--- Get a color of the dot with the specified coordinates uint GetPixel(const int x,const int y) const { return this.m_canvas.PixelGet(x,y); } //+------------------------------------------------------------------+
CCanvasクラスのPixelGet()メソッドを呼び出した結果がここに返されます。このメソッドは、色をARGB形式で返します。
以下は、ラスターデータの入力、クリア、更新を行うメソッドです。
//+------------------------------------------------------------------+ //| The methods of filling, clearing and updating raster data | //+------------------------------------------------------------------+ //--- Clear the element filling it with color and opacity void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element completely void Erase(const bool redraw=false); //--- Update the element void Update(const bool redraw=false) { this.m_canvas.Update(redraw); } //+------------------------------------------------------------------+
Update()メソッドは、CCanvasクラスのUpdate()メソッドを使用してオブジェクトとチャートを更新するだけです。
Erase()メソッドは、クラス本体の外部に実装されています。
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CGCnvElement::Erase(const color colour,const uchar opacity,const bool redraw=false) { this.m_canvas.Erase(::ColorToARGB(colour,opacity)); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Clear the element completely | //+------------------------------------------------------------------+ void CGCnvElement::Erase(const bool redraw=false) { this.m_canvas.Erase(NULL_COLOR); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+
これらは2つのオーバーロードされたメソッドです。
最初の例では、CCanvasクラスのErase()メソッドを使用して、要素全体を塗りつぶすために使用される必要な色と不透明度を渡します。このメソッドは、ColorToARGB()関数による値のARGB形式への変換を使用して、CCanvasクラスのErase()メソッドに渡された色と不透明度のレベルを適用することに注意してください。これはすべての描画メソッドで行います。
2番目のメソッドでは、背景全体を完全に透明な黒色で塗りつぶします。その値は、以前はNULL_COLORマクロ置換を使用して定義されていました。
各メソッドは、チャートを再描画する必要があることを示すフラグを受け取ります。フラグが設定されている場合、チャートは再描画されます。
次は、平滑化せずにプリミティブを描画するためのメソッドのブロックです。すべてのメソッドは同一であり、CCanvasクラスの対応するメソッドを呼び出します。これらのメソッドは、メソッド引数で指定されたパラメータと、メソッドに渡された色と透明度の値からARGB形式に変換された色を受け取ります。
//+------------------------------------------------------------------+ //| The methods of drawing primitives without smoothing | //+------------------------------------------------------------------+ //--- Set the color of the dot with the specified coordinates void SetPixel(const int x,const int y,const color clr,const uchar opacity=255) { this.m_canvas.PixelSet(x,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a vertical line void DrawLineVertical(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.LineVertical(x,y1,y2,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a horizontal line void DrawLineHorizontal(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.LineHorizontal(x1,x2,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a freehand line void DrawLine(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Line(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a polyline void DrawPolyline(int &array_x[], // Array with the X coordinates of polyline points int & array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Polyline(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a polygon void DrawPolygon(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Polygon(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a rectangle using two points void DrawRectangle(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // color const uchar opacity=255) // Opacity { this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a circle void DrawCircle(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Circle(x,y,r,::ColorToARGB(clr,opacity)); } //--- Draw a triangle void DrawTriangle(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255) // Opacity { m_canvas.Triangle(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity)); } //--- Draw an ellipse using two points void DrawEllipse(const int x1, // X coordinate of the first point defining the ellipse const int y1, // Y coordinate of the first point defining the ellipse const int x2, // X coordinate of the second point defining the ellipse const int y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Ellipse(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw an arc of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) void DrawArc(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255) // Opacity { m_canvas.Arc(x1,y1,x2,y2,x3,y3,x4,y4,::ColorToARGB(clr,opacity)); } //--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) void DrawPie(const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Line color const color fill_clr, // Fill color const uchar opacity=255) // Opacity { this.m_canvas.Pie(x1,y1,x2,y2,x3,y3,x4,y4,::ColorToARGB(clr,opacity),ColorToARGB(fill_clr,opacity)); } //+------------------------------------------------------------------+
以下は、平滑化せずに塗りつぶされたプリミティブを描画するためのメソッドのブロックです。
//+------------------------------------------------------------------+ //| The methods of drawing filled primitives without smoothing | //+------------------------------------------------------------------+ //--- Fill in the area void Fill(const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0) // Threshold { this.m_canvas.Fill(x,y,::ColorToARGB(clr,opacity),threshould); } //--- Draw a filled rectangle void DrawRectangleFill(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a filled circle void DrawCircleFill(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillCircle(x,y,r,::ColorToARGB(clr,opacity)); } //--- Draw a filled triangle void DrawTriangleFill(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillTriangle(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity)); } //--- Draw a filled polygon void DrawPolygonFill(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillPolygon(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates void DrawEllipseFill(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillEllipse(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //+------------------------------------------------------------------+
以下は、平滑化を使用してプリミティブを描画するメソッドです。
//+------------------------------------------------------------------+ //| The methods of drawing primitives using smoothing | //+------------------------------------------------------------------+ //--- Draw a point using AntiAliasing algorithm void SetPixelAA(const double x, // Point X coordinate const double y, // Point Y coordinate const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.PixelSetAA(x,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a freehand line using AntiAliasing algorithm void DrawLineAA(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.LineAA(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draw a segment of a freehand line using Wu algorithm void DrawLineWu(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.LineWu(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draws a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThick(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThick(x1,y1,x2,y2,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThickVertical(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThickVertical(x,y1,y2,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a horizontal segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThickHorizontal(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThickHorizontal(x1,x2,y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draws a polyline using AntiAliasing algorithm void DrawPolylineAA(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolylineAA(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draws a polyline using Wu algorithm void DrawPolylineWu(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolylineWu(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polyline with a specified width consecutively using two antialiasing algorithms. //--- First, individual line segments are smoothed based on Bezier curves. //--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality void DrawPolylineSmooth(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const ENUM_LINE_STYLE style=STYLE_SOLID,// Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolylineSmooth(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style,tension,step); } //--- Draw a polyline having a specified width using smoothing algorithm with the preliminary filtration void DrawPolylineThick(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolylineThick(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a polygon using AntiAliasing algorithm void DrawPolygonAA(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolygonAA(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polygon using Wu algorithm void DrawPolygonWu(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolygonWu(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polygon with a specified width consecutively using two smoothing algorithms. //--- First, individual segments are smoothed based on Bezier curves. //--- Then, the raster smoothing algorithm is applied to the polygon built from these segments to improve the rendering quality. void DrawPolygonSmooth(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const ENUM_LINE_STYLE style=STYLE_SOLID,// Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolygonSmooth(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style,tension,step); } //--- Draw a polygon having a specified width using smoothing algorithm with the preliminary filtration void DrawPolygonThick(const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const uint style=STYLE_SOLID,// line style ENUM_LINE_END end_style=LINE_END_ROUND) // line ends style { this.m_canvas.PolygonThick(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a triangle using AntiAliasing algorithm void DrawTriangleAA(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.TriangleAA(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity),style); } //--- Draw a triangle using Wu algorithm void DrawTriangleWu(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.TriangleWu(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity),style); } //--- Draw a circle using AntiAliasing algorithm void DrawCircleAA(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.CircleAA(x,y,r,::ColorToARGB(clr,opacity),style); } //--- Draw a circle using Wu algorithm void DrawCircleWu(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.CircleWu(x,y,r,::ColorToARGB(clr,opacity),style); } //--- Draw an ellipse by two points using AntiAliasing algorithm void DrawEllipseAA(const double x1, // X coordinate of the first point defining the ellipse const double y1, // Y coordinate of the first point defining the ellipse const double x2, // X coordinate of the second point defining the ellipse const double y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.EllipseAA(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draw an ellipse by two points using Wu algorithm void DrawEllipseWu(const int x1, // X coordinate of the first point defining the ellipse const int y1, // Y coordinate of the first point defining the ellipse const int x2, // X coordinate of the second point defining the ellipse const int y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.EllipseWu(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //+------------------------------------------------------------------+
追加されたすべてのメソッドのロジックは完全に透過的です。メソッドに渡されるすべてのパラメーターはsignedです。各メソッドの目的はコメントに記載されているので、ここではすべてが明確だと思います。いずれにせよ、コメントセクションを使用してください。
テキスト操作メソッド
CCanvasクラスは、フォント、色、透明度など、最後に表示されたテキストの設定を記憶しています。テキストサイズを確認するには、TextSize()メソッドを使用して、現在のフォント設定を適用し、テキスト境界の長方形の幅と高さを測定します。これは、背景色を使用して前のテキストを上書きしたり、新しい座標(テキストオフセット)を使用して書き込んだりする場合など、いくつかの場合に必要になることがあります。この場合、テキスト座標だけでなく、テキストアンカーポイント(左上、中央上、右上など)も必要です。境界長方形のどのアンカー角度がテキストに与えられているかを正確に知る必要があります。さもないと、消知ゴムの長方形の座標が正しく設定されません。これを実現するには、最後に指定されたアンカーポイントを格納するクラスメンバー変数を追加する必要があります。
クラスのprivateセクションで変数を宣言します。
long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties ENUM_TEXT_ANCHOR m_text_anchor; // Current text alignment //--- Return the index of the array the order (1) double and (2) string properties are located at int IndexProp(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_CANV_ELEMENT_PROP_STRING property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_DOUBLE_TOTAL; }
パラメトリッククラスコンストラクタの最初に、オブジェクトタイプの値とテキストアンカーポイントを初期化します。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) { this.m_name=this.m_name_prefix+name; this.m_chart_id=chart_id; this.m_subwindow=wnd_num; this.m_type=element_type; this.m_text_anchor=0; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) {
Type()仮想メソッドによって返されるm_type親クラス変数に、コンストラクタパラメータで渡されたオブジェクトタイプを追加し、m_text_anchor変数はデフォルト値(境界矩形の左上隅)を使用して初期化されます。
クラス本体の最後(つまり、プリミティブを操作するためのコードブロックの後)に、テキストを操作するためのコードブロックを追加します。
//+------------------------------------------------------------------+ //| The methods of working with text | //+------------------------------------------------------------------+ //--- Return text the alignment type (anchor method) ENUM_TEXT_ANCHOR TextAnchor(void) const { return this.m_text_anchor; } //--- Set the current font bool SetFont(const string name, // Font name. For example, "Arial" const int size, // Font size const uint flags=0, // Font creation flags const uint angle=0, // Font slope angle in tenths of a degree const bool relative=true) // Relative font size flag { return this.m_canvas.FontSet(name,(relative ? size*-10 : size),flags,angle); } //--- Set a font name bool SetFontName(const string name) // Font name. For example, "Arial" { return this.m_canvas.FontNameSet(name); } //--- Set a font size bool SetFontSize(const int size, // Font size const bool relative=true) // Relative font size flag { return this.m_canvas.FontSizeSet(relative ? size*-10 : size); } //--- Set font flags //--- FONT_ITALIC - Italic, FONT_UNDERLINE - Underline, FONT_STRIKEOUT - Strikeout bool SetFontFlags(const uint flags) // Font creation flags { return this.m_canvas.FontFlagsSet(flags); } //--- Set a font slope angle bool SetFontAngle(const float angle) // Font slope angle in tenths of a degree { return this.m_canvas.FontAngleSet(uint(angle*10)); } //--- Set the font anchor angle (alignment type) void SetTextAnchor(const uint flags=0) { this.m_text_anchor=(ENUM_TEXT_ANCHOR)flags; } //--- Gets the current font parameters and write them to variables void GetFont(string &name, // The reference to the variable for returning a font name int &size, // Reference to the variable for returning a font size uint &flags, // Reference to the variable for returning font flags uint &angle) // Reference to the variable for returning a font slope angle { this.m_canvas.FontGet(name,size,flags,angle); } //--- Return (1) the font name, (2) size, (3) flags and (4) slope angle string FontName(void) const { return this.m_canvas.FontNameGet(); } int FontSize(void) const { return this.m_canvas.FontSizeGet(); } int FontSizeRelative(void) const { return(this.FontSize()<0 ? -this.FontSize()/10 : this.FontSize()); } uint FontFlags(void) const { return this.m_canvas.FontFlagsGet(); } uint FontAngle(void) const { return this.m_canvas.FontAngleGet(); } //--- Return the text (1) width, (2) height and (3) all sizes (the current font is used to measure the text) int TextWidth(const string text) { return this.m_canvas.TextWidth(text); } int TextHeight(const string text) { return this.m_canvas.TextHeight(text); } void TextSize(const string text, // Text for measurement int &width, // Reference to the variable for returning a text width int &height) // Reference to the variable for returning a text height { this.m_canvas.TextSize(text,width,height); } //--- Display the text in the current font void Text(int x, // X coordinate of the text anchor point int y, // Y coordinate of the text anchor point string text, // Display text const color clr, // Color const uchar opacity=255, // Opacity uint alignment=0) // Text anchoring method { this.m_text_anchor=(ENUM_TEXT_ANCHOR)alignment; this.m_canvas.TextOut(x,y,text,::ColorToARGB(clr,opacity),alignment); } }; //+------------------------------------------------------------------+
ここでは、すべてがプリミティブの処理に似ています。すべてのメソッドの目的、入力、出力はコメントされています。
現在のフォントパラメータの設定方法についてコメントしたいとおもいます。
//--- Set the current font bool SetFont(const string name, // Font name. For example, "Arial" const int size, // Font size const uint flags=0, // Font creation flags const uint angle=0, // Font slope angle in tenths of a degree const bool relative=true) // Relative font size flag { return this.m_canvas.FontSet(name,(relative ? size*-10 : size),flags,angle);
ここで、sizeはフォントサイズを指定し、通常のOBJ_LABELテキストラベルオブジェクトを使用してテキストを表示するときに設定するフォントサイズに従って常に設定されます。サイズは正の整数値として設定されます。それとは逆に、キャンバスにテキストを描画する場合、フォントサイズはTextSetFont()関数と同じ方法で設定されます。
フォントサイズは、正または負の値を使用して設定されます。記号は、テキストサイズがオペレーティングシステムの設定(フォントスケール)に依存するかどうかを定義します。
- サイズが正の場合、論理フォントを物理フォントとして表示するときに、デバイスの物理単位(ピクセル)に変換されます。サイズは、使用可能なフォントのシンボルセルの高さに対応します。TextOut()関数を使用して表示されるテキストとOBJ_LABEL(「テキストラベル」)グラフィカルオブジェクトを使用して表示されるテキストを共有して使用する場合はお勧めしません。
- サイズが負の場合、論理ポイントの10分の1に設定されていると見なされ(値-350は35論理ポイントに等しい)、10で除算されます。結果の値は、デバイスの物理単位(ピクセル)に変換され、使用可能なフォントからの文字の高さの絶対値に対応します。画面上のOBJ_LABELオブジェクトサイズのテキストを取得するには、オブジェクトプロパティで指定されたフォントサイズに-10を掛けます。
フォントの相対サイズフラグはメソッドで確認されます。設定されている場合(デフォルト)、size値に-10が乗算されるため、CCanvasクラスをFontSet()メソッドに渡すための正しい値を使用してフォントが指定されます。
パラメトリッククラスコンストラクタで、フォントの初期化を追加します(デフォルトの名前とサイズを設定します)。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) { this.m_name=this.m_name_prefix+name; this.m_chart_id=chart_id; this.m_subwindow=wnd_num; this.m_type=element_type; this.SetFont("Calibri",8); this.m_text_anchor=0; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) {
現在の記事で計画している改善点はこれですべてです。結果をテストしてみましょう。
検証
チャートに2つのグラフィック要素オブジェクトを表示する前の記事のEAがあるので、新しい\MQL5\Experts\TestDoEasy\Part75\フォルダでTestDoEasyPart75.mq5として保存します。
最初の(一番上の)オブジェクトをクリックすると、長方形と円が交互に描画されます。オブジェクトを新たにクリックするたびに、長方形のサイズは各辺で2ピクセルずつ減少し、円の半径も2ピクセルずつ減少します。長方形は通常の方法で描画され、円は平滑化を使用して描画されます。新しいクリックごとに、オブジェクトの不透明度が0から255に増加して繰り返します。
2番目(下)のオブジェクトをクリックすると、アンカーポイントを交互に変更するテキストが表示されます。テキスト自体にアンカーポイント名を記述します。オブジェクトの透明度は変更されません。
作成された要素の数を指定します。
//+------------------------------------------------------------------+ //| TestDoEasyPart75.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //--- includes #include <Arrays\ArrayObj.mqh> #include <DoEasy\Services\Select.mqh> #include <DoEasy\Objects\Graph\GCnvElement.mqh> //--- defines #define ELEMENTS_TOTAL (2) // Number of created グラフィック要素s //--- input parameters sinput bool InpMovable = true; // Movable flag //--- global variables CArrayObj list_elements; //+------------------------------------------------------------------+
時間枠が変更されるたびに同一のオブジェクトが作成されないようにするには、新しいオブジェクトを作成する前に、OnInit()ハンドラで作成済みのオブジェクトのリストをクリアします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the permissions to send cursor movement and mouse scroll events ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true); ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true); //--- Set EA global variables //--- Create the specified number of graphical elements on the canvas list_elements.Clear(); int total=ELEMENTS_TOTAL; for(int i=0;i<total;i++) { //--- When creating an object, pass all the required parameters to it CGCnvElement *element=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,i,0,ChartID(),0,"Element_0"+(string)(i+1),300,40+(i*80),100,70,clrSilver,200,InpMovable,true,true); if(element==NULL) continue; //--- Add objects to the list if(!list_elements.Add(element)) { delete element; continue; } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
指定した名前で新しいオブジェクトを作成する必要性を確認するグラフィカルオブジェクトコレクションクラスがまだないため、ここでは、これらのオブジェクトを新たに再作成し、以前に作成したオブジェクトのリストを事前にクリアします。
OnChartEvent()ハンドラは、作成された2つのオブジェクトに対するマウスクリックの処理を受け取ります。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If clicking on an object if(id==CHARTEVENT_OBJECT_CLICK) { //--- In the new list, get the element object with the name corresponding to the sparam string parameter value of the OnChartEvent() handler CArrayObj *obj_list=CSelect::ByGraphCanvElementProperty(GetPointer(list_elements),CANV_ELEMENT_PROP_NAME_OBJ,sparam,EQUAL); //--- If the object is received from the list if(obj_list!=NULL && obj_list.Total()>0) { static uchar try0=0, try1=0; //--- Get the pointer to the object in the list CGCnvElement *obj=obj_list.At(0); //--- If this is the first graphical element if(obj.ID()==0) { //--- Set a new opacity level for the object uchar opasity=obj.Opacity(); if((opasity+5)>255) opasity=0; else opasity+=5; //--- Set a new opacity to the object obj.SetOpacity(opasity); //--- Set rectangle and circle coordinates int x1=2,x2=obj.Width()-3; int y1=2,y2=obj.Height()-3; int xC=(x1+x2)/2; int yC=(y1+y2)/2; int R=yC-y1; //--- Draw a rectangle at each first click if(try0%2==0) obj.DrawRectangle(x1+try0,y1+try0,x2-try0,y2-try0,clrDodgerBlue,obj.Opacity()); //--- Display the circle smoothed using AntiAliasing at each second click else obj.DrawCircleAA(xC,yC,R-try0,clrGreen,obj.Opacity()); //--- If the number of clicks on the object exceeds 30 if(try0>30) { //--- Clear the object setting its current color and transparency obj.Erase(obj.ColorBG(),obj.Opacity()); //--- Re-start the click number countdown try0=0; } //--- Update the chart and the object, and display the comment featuring color values and object opacity obj.Update(true); // 'true' is not needed here since the next Comment command redraws the chart anyway Comment("Object name: ",obj.NameObj(),", opasity=",obj.Opacity(),", Color BG: ",(string)obj.ColorBG()); //--- Increase the counter of mouse clicks by object try0++; } //--- If this is the second object else if(obj.ID()==1) { //--- Set the font parameters for it ("Calibri" size 8) obj.SetFont("Calibri",8); //--- Set the text anchor angle corresponding to the click counter by object obj.SetTextAnchor((ENUM_TEXT_ANCHOR)try1); //--- Create the text out of the anchor angle name string text=StringSubstr(EnumToString(obj.TextAnchor()),12); //--- Set the text coordinates relative to the upper left corner of the graphical element int xT=2,yT=2; //--- Depending on the anchor angle, set the new coordinates of the displayed text //--- LEFT_TOP if(try1==0) { xT=2; yT=2; } //--- CENTER_TOP else if(try1==1) { xT=obj.Width()/2; yT=2; } //--- RIGHT_TOP //--- since the ENUM_TEXT_ANCHOR enumeration features no 3, increase the counter of object clicks by 1 else if(try1==2) { xT=obj.Width()-2; yT=2; try1++; } //--- LEFT_CENTER else if(try1==4) { xT=2; yT=obj.Height()/2; } //--- CENTER else if(try1==5) { xT=obj.Width()/2; yT=obj.Height()/2; } //--- RIGHT_CENTER //--- since the ENUM_TEXT_ANCHOR enumeration features no 7, increase the counter of object clicks by 1 else if(try1==6) { xT=obj.Width()-2; yT=obj.Height()/2; try1++; } //--- LEFT_BOTTOM else if(try1==8) { xT=2; yT=obj.Height()-2; } //--- CENTER_BOTTOM else if(try1==9) { xT=obj.Width()/2; yT=obj.Height()-2; } //--- RIGHT_BOTTOM else if(try1==10) { xT=obj.Width()-2; yT=obj.Height()-2; } //--- Clear the graphical element filling it with the current color and transparency obj.Erase(obj.ColorBG(),obj.Opacity()); //--- Display the text with the calculated coordinates in the cleared element obj.Text(xT,yT,text,clrDodgerBlue,255,obj.TextAnchor()); //--- Update the object and chart obj.Update(true); // 'true' is not needed here since the next Comment command redraws the chart anyway Comment("Object name: ",obj.NameObj(),", opasity=",obj.Opacity(),", Color BG: ",(string)obj.ColorBG()); //--- Increase the counter of object clicks try1++; if(try1>10) try1=0; } } } } //+------------------------------------------------------------------+
ハンドラコードには詳細なコメントが含まれています。その論理は明らかだと思います。現在の記事に関連する質問がある場合は、以下のコメントで遠慮なく質問してください。
EAをコンパイルし、チャート上で起動します。オブジェクトをクリックします。
その結果、CDに似た面白い画像が上部オブジェクトに誤ってできてしまいました)
次の段階
次の記事では、ここで作成したグラフィック要素オブジェクトの子孫であるオブジェクトの開発を開始します。
ライブラリの現在のバージョンのすべてのファイルは、テストおよびダウンロードできるように、MQL5のテストEAファイルと一緒に以下に添付されています。
質問や提案はコメント欄にお願いします。
**連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第73部): グラフィック要素のフォームオブジェクト
DoEasyライブラリのグラフィックス(第74部): CCanvasクラスを使用した基本的グラフィック要素
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/9515
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索