
DoEasyライブラリのグラフィックス(第80部): 「幾何学的アニメーションフレーム」オブジェクトクラス
内容
概念
本稿では、キャンバスに図形を描くことを目的としたクラスの作業を続けます。画像が重ねられる背景を維持しながら、キャンバスの特定の領域に単一のアニメーションフレームを描画するためのアニメーションフレームクラスはすでに作成しました。画像を削除または変更するときに背景を復元できます。これらの事前に作成されたフレームにより、フレームをすばやく変更するためのアニメーションシーケンスを作成できます。単一のフレームでは、その領域内でアニメーションを作成することもできます。
今日は、以前に作成されたこれらのクラスのコードを少し最適化します。コードの繰り返しセクションがある場合、それらのすべてのロジックを個別の関数/メソッドに形式化することができ(そして形式化する必要があり)、呼び出されるという概念に従います。これにより、コードが読みやすくなり、ボリュームが減少します。
さらに、幾何学的アニメーションフレームのオブジェクトクラスを作成します。これは何を意味するのでしょうか。
さまざまな多角形を作成するメソッドはすでにたくさんありますが、正多角形を描画する必要がある場合は、頂点の座標を手動で計算するよりも、幾何学を使用する方がはるかに簡単です。後に他の幾何学的図形を追加すると思いますが、これらの頂点座標は、手動で設定するのではなく、方程式を使用して計算できます。
ウィキペディアによると:
正多角形は、等角(すべての角度が等しい)かつ等辺(すべての辺の長さが同じ)の多角形です。例
正八角形
正多角形は円に内接します。このような円は外接円と呼ばれます。円は多角形のすべての頂点を通ります。
外接円
内接円もあります。これは多角形に内接する円です。この場合、多角形のすべての辺が円周に接触します。
内接円
円が内接する正方形を除いて、そのような多角形は考慮しません。正多角形が円に内接します。
正方形はアニメーションフレームを表します(左上隅の座標とその辺のサイズ(長さ))。直径がアニメーションフレームの正方形の辺の長さに等しい円は、頂点が円周に接する内接多角形を特徴とします。
したがって、多角形座標の配列を作成する必要はありません。代わりに、必要な頂点の数、左上隅の座標、および正方形の辺の長さを指定するだけで済みます。
ライブラリクラスの改善
\MQL5\Include\DoEasy\Data.mqhに、新しいメッセージインデックスを追加します。
//--- CGCnvElement MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY, // Error! Empty array MSG_CANV_ELEMENT_ERR_ARRAYS_NOT_MATCH, // Error! Array-copy of the resource does not match the original //--- CForm
また、新しく追加したインデックスに対応するメッセージテキストも追加します。
//--- CGCnvElement {"Ошибка! Пустой массив","Error! Empty array"}, {"Ошибка! Массив-копия ресурса не совпадает с оригиналом","Error! Array-copy of the resource does not match the original"}, //--- CForm
アニメーションフレームの配置(アンカー角度)は、すべてのアニメーションフレーム(テキスト、長方形、幾何学など)に依存するようになるため、列挙型とその定数の名前を少し変更して、テキストではなくフレームに関連付けることにしました。
\MQL5\Include\DoEasy\Defines.mqhのアンカー角度の列挙で、「TEXT」を「 FRAME」に置き換えます。
//+------------------------------------------------------------------+ //| Data for handling graphical elements | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of anchoring methods | //| (horizontal and vertical text alignment) | //+------------------------------------------------------------------+ enum ENUM_FRAME_ANCHOR { FRAME_ANCHOR_LEFT_TOP = 0, // Frame anchor point at the upper left corner of the bounding rectangle FRAME_ANCHOR_CENTER_TOP = 1, // Frame anchor point at the top center side of the bounding rectangle FRAME_ANCHOR_RIGHT_TOP = 2, // Frame anchor point at the upper right corner of the bounding rectangle FRAME_ANCHOR_LEFT_CENTER = 4, // Frame anchor point at the center of the left side of the bounding rectangle FRAME_ANCHOR_CENTER = 5, // Frame anchor point at the center of the bounding rectangle FRAME_ANCHOR_RIGHT_CENTER = 6, // Frame anchor point at the center of the right side of the bounding rectangle FRAME_ANCHOR_LEFT_BOTTOM = 8, // Frame anchor point at the lower left corner of the bounding rectangle FRAME_ANCHOR_CENTER_BOTTOM = 9, // Frame anchor point at the bottom center side of the bounding rectangle FRAME_ANCHOR_RIGHT_BOTTOM = 10, // Frame anchor point at the lower right corner of the bounding rectangle }; //+------------------------------------------------------------------+
アニメーションフレーム型の列挙で、新しい型を追加します。これは、幾何学的図形アニメーションフレームです。
//+------------------------------------------------------------------+ //| Data for working with graphical element animation | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of animation frame types | //+------------------------------------------------------------------+ enum ENUM_ANIMATION_FRAME_TYPE { ANIMATION_FRAME_TYPE_TEXT, // Text animation frame ANIMATION_FRAME_TYPE_QUAD, // Rectangular animation frame ANIMATION_FRAME_TYPE_GEOMETRY, // Square animation frame of geometric shapes }; //+------------------------------------------------------------------+
描画された形状型のリストで、前の記事で実装するのを忘れた塗りつぶされた領域を追加します。
//+------------------------------------------------------------------+ //| List of drawn shape types | //+------------------------------------------------------------------+ enum ENUM_FIGURE_TYPE { FIGURE_TYPE_PIXEL, // Pixel FIGURE_TYPE_PIXEL_AA, // Pixel with antialiasing FIGURE_TYPE_LINE_VERTICAL, // Vertical line FIGURE_TYPE_LINE_VERTICAL_THICK, // a Vertical segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_LINE_HORIZONTAL, // Horizontal line FIGURE_TYPE_LINE_HORIZONTAL_THICK, // Horizontal segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_LINE, // Arbitrary line FIGURE_TYPE_LINE_AA, // Line with antialiasing FIGURE_TYPE_LINE_WU, // Line with WU smoothing FIGURE_TYPE_LINE_THICK, // Segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_POLYLINE, // Polyline FIGURE_TYPE_POLYLINE_AA, // Polyline with antialiasing FIGURE_TYPE_POLYLINE_WU, // Polyline with WU smoothing FIGURE_TYPE_POLYLINE_SMOOTH, // Polyline with a specified width using two smoothing algorithms FIGURE_TYPE_POLYLINE_THICK, // Polyline with a specified width using a smoothing algorithm FIGURE_TYPE_POLYGON, // Polygon FIGURE_TYPE_POLYGON_FILL, // Filled polygon FIGURE_TYPE_POLYGON_AA, // Polygon with antialiasing FIGURE_TYPE_POLYGON_WU, // Polygon with WU smoothing FIGURE_TYPE_POLYGON_SMOOTH, // Polygon with a specified width using two smoothing algorithms FIGURE_TYPE_POLYGON_THICK, // Polygon with a specified width using a smoothing algorithm FIGURE_TYPE_RECTANGLE, // Rectangle FIGURE_TYPE_RECTANGLE_FILL, // Filled rectangle FIGURE_TYPE_CIRCLE, // Circle FIGURE_TYPE_CIRCLE_FILL, // Filled circle FIGURE_TYPE_CIRCLE_AA, // Circle with antialiasing FIGURE_TYPE_CIRCLE_WU, // Circle with WU smoothing FIGURE_TYPE_TRIANGLE, // Triangle FIGURE_TYPE_TRIANGLE_FILL, // Filled triangle FIGURE_TYPE_TRIANGLE_AA, // Triangle with antialiasing FIGURE_TYPE_TRIANGLE_WU, // Triangle with WU smoothing FIGURE_TYPE_ELLIPSE, // Ellipse FIGURE_TYPE_ELLIPSE_FILL, // Filled ellipse FIGURE_TYPE_ELLIPSE_AA, // Ellipse with antialiasing FIGURE_TYPE_ELLIPSE_WU, // Ellipse with WU smoothing FIGURE_TYPE_ARC, // Ellipse arc FIGURE_TYPE_PIE, // Ellipse sector FIGURE_TYPE_FILL, // Filled area }; //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhで、グラフィカルリソースのコピーを格納するための配列の名前をよりわかりやすい名前に置き換えます。 配列名はかなり紛らわしく、最初に作成されたフォームのコピーを格納するためにどの配列を使用するかを定義するのが難しいためです。また、グラフィカルリソースを配列に保存するメソッドをクラスのprotectedセクションから削除します。
//+------------------------------------------------------------------+ //| Class of the graphical element object | //+------------------------------------------------------------------+ class CGCnvElement : public CGBaseObj { protected: CCanvas m_canvas; // CCanvas class object CPause m_pause; // Pause class object bool m_shadow; // Shadow presence color m_chart_color_bg; // Chart background color uint m_duplicate_res[]; // Array for storing resource data copy //--- Return the cursor position relative to the (1) entire element and (2) the element's 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); //--- Save the graphical resource to the array bool ResourceCopy(const string source); private:
クラスの「TEXT_ANCHOR」を「FRAME_ANCHOR」に置き換えます(または、すべてのライブラリファイルを一度に置き換えればさらにいいです)。すべてのライブラリファイル内のすべての発生個所を検索するには、Shift + Ctrl + Hを押して、新しいウィンドウで次の検索と置換の条件を設定します。
[Folder:]フィールドには、エディターの場所に基づいたパスが表示されます。
クラスのpublicセクションで、グラフィカルリソースを配列に保存してそこからリソースを復元するためのメソッドを宣言し、キャンバスを更新するためのメソッドと返すグラフィカルリソースコピー配列のサイズをメソッドを記述します。
public: //--- Set object's (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’s (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; } //--- Return itself CGCnvElement *GetObject(void) { return &this; } //--- 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); //--- (1) Save the graphical resource to the array and (2) restore the resource from the array bool ResourceStamp(const string source); virtual bool Reset(void); //--- Create the element bool Create(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 redraw=false); //--- Return the pointer to a canvas object CCanvas *GetCanvasObj(void) { return &this.m_canvas; } //--- Set the canvas update frequency void SetFrequency(const ulong value) { this.m_pause.SetWaitingMSC(value); } //--- Update the canvas void CanvasUpdate(const bool redraw=false) { this.m_canvas.Update(redraw); } //--- Return the size of the graphical resource copy array uint DuplicateResArraySize(void) { return ::ArraySize(this.m_duplicate_res); } //--- Update the coordinates (shift the canvas) bool Move(const int x,const int y,const bool redraw=false); //--- Save an image to the array bool ImageCopy(const string source,uint &array[]); //--- Change the lightness of (1) ARGB and (2) COLOR by a specified amount uint ChangeColorLightness(const uint clr,const double change_value); color ChangeColorLightness(const color colour,const double change_value); //--- Change the saturation of (1) ARGB and (2) COLOR by a specified amount uint ChangeColorSaturation(const uint clr,const double change_value); color ChangeColorSaturation(const color colour,const double change_value); protected:
以前のResourceCopy()メソッドはResourceStamp()と呼ばれるようになりました。
//+------------------------------------------------------------------+ //| Save the graphical resource to the array | //+------------------------------------------------------------------+ bool CGCnvElement::ResourceStamp(const string source) { return this.ImageCopy(DFUN,this.m_duplicate_res); } //+------------------------------------------------------------------+
以下は、グラフィカルリソースを配列から取得するメソッドです。
//+------------------------------------------------------------------+ //| Restore the graphical resource from the array | //+------------------------------------------------------------------+ bool CGCnvElement::Reset(void) { //--- Get the size of the graphical resource copy array int size=::ArraySize(this.m_duplicate_res); //--- If the array is empty, inform of that and return 'false' if(size==0) { CMessage::ToLog(DFUN,MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY); return false; } //--- If the size of the graphical resource copy array does not match the size of the graphical resource, //--- inform of that in the journal and return 'false' if(this.m_canvas.Width()*this.m_canvas.Height()!=size) { CMessage::ToLog(DFUN,MSG_CANV_ELEMENT_ERR_ARRAYS_NOT_MATCH); return false; } //--- Set the index of the array for setting the image pixel int n=0; //--- In the loop by the resource height, for(int y=0;y<this.m_canvas.Height();y++) { //--- in the loop by the resource width for(int x=0;x<this.m_canvas.Width();x++) { //--- Restore the next image pixel from the array and increase the array index this.m_canvas.PixelSet(x,y,this.m_duplicate_res[n]); n++; } } //--- Update the data on the canvas and return 'true' this.m_canvas.Update(false); return true; } //+------------------------------------------------------------------+
メソッドのロジックは、コードのコメントで説明されています。つまり、リソースコピー配列のサイズを確認します。空の場合、またはコピーサイズが元のサイズと一致しない場合は、操作ログにエラーを報告して、メソッドを終了します。次に、すべてのデータを配列からキャンバスにピクセルごとにコピーします。
リソース配列のコピーの名前とグラフィカルリソースを配列に保存するメソッドを変更したので、\MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh影オブジェクトクラスファイルで修正を行う必要があります。
修正はGaussianBlur()メソッドにのみ関係します。
//+------------------------------------------------------------------+ //| Gaussian blur | //| https://www.mql5.com/ja/articles/1612#chapter4 | //+------------------------------------------------------------------+ bool CShadowObj::GaussianBlur(const uint radius) { //--- int n_nodes=(int)radius*2+1; //--- Read graphical resource data. If failed, return false if(!CGCnvElement::ResourceStamp(DFUN)) return false; //--- Check the blur amount. If the blur radius exceeds half of the width or height, return 'false' if((int)radius>=this.Width()/2 || (int)radius>=this.Height()/2) { ::Print(DFUN,CMessage::Text(MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE)); return false; } //--- Decompose image data from the resource into a, r, g, b color components int size=::ArraySize(this.m_duplicate_res); //--- arrays for storing A, R, G and B color components //--- for horizontal and vertical blur uchar a_h_data[],r_h_data[],g_h_data[],b_h_data[]; uchar a_v_data[],r_v_data[],g_v_data[],b_v_data[]; //--- Change the size of component arrays according to the array size of the graphical resource data if(::ArrayResize(a_h_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"a_h_data\""); return false; } if(::ArrayResize(r_h_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"r_h_data\""); return false; } if(::ArrayResize(g_h_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"g_h_data\""); return false; } if(ArrayResize(b_h_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"b_h_data\""); return false; } if(::ArrayResize(a_v_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"a_v_data\""); return false; } if(::ArrayResize(r_v_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"r_v_data\""); return false; } if(::ArrayResize(g_v_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"g_v_data\""); return false; } if(::ArrayResize(b_v_data,size)==-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); ::Print(DFUN_ERR_LINE,": \"b_v_data\""); return false; } //--- Declare the array for storing blur weight ratios and, //--- if failed to get the array of weight ratios, return 'false' double weights[]; if(!this.GetQuadratureWeights(1,n_nodes,weights)) return false; //--- Set components of each image pixel to the color component arrays for(int i=0;i<size;i++) { a_h_data[i]=GETRGBA(this.m_duplicate_res[i]); r_h_data[i]=GETRGBR(this.m_duplicate_res[i]); g_h_data[i]=GETRGBG(this.m_duplicate_res[i]); b_h_data[i]=GETRGBB(this.m_duplicate_res[i]); } //--- Blur the image horizontally (along the X axis) uint XY; // Pixel coordinate in the array double a_temp=0.0,r_temp=0.0,g_temp=0.0,b_temp=0.0; int coef=0; int j=(int)radius; //--- Loop by the image width for(int Y=0;Y<this.Height();Y++) { //--- Loop by the image height for(uint X=radius;X<this.Width()-radius;X++) { XY=Y*this.Width()+X; a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0; coef=0; //--- Multiply each color component by the weight ratio corresponding to the current image pixel for(int i=-1*j;i<j+1;i=i+1) { a_temp+=a_h_data[XY+i]*weights[coef]; r_temp+=r_h_data[XY+i]*weights[coef]; g_temp+=g_h_data[XY+i]*weights[coef]; b_temp+=b_h_data[XY+i]*weights[coef]; coef++; } //--- Save each rounded color component calculated according to the ratios to the component arrays a_h_data[XY]=(uchar)::round(a_temp); r_h_data[XY]=(uchar)::round(r_temp); g_h_data[XY]=(uchar)::round(g_temp); b_h_data[XY]=(uchar)::round(b_temp); } //--- Remove blur artifacts to the left by copying adjacent pixels for(uint x=0;x<radius;x++) { XY=Y*this.Width()+x; a_h_data[XY]=a_h_data[Y*this.Width()+radius]; r_h_data[XY]=r_h_data[Y*this.Width()+radius]; g_h_data[XY]=g_h_data[Y*this.Width()+radius]; b_h_data[XY]=b_h_data[Y*this.Width()+radius]; } //--- Remove blur artifacts to the right by copying adjacent pixels for(int x=int(this.Width()-radius);x<this.Width();x++) { XY=Y*this.Width()+x; a_h_data[XY]=a_h_data[(Y+1)*this.Width()-radius-1]; r_h_data[XY]=r_h_data[(Y+1)*this.Width()-radius-1]; g_h_data[XY]=g_h_data[(Y+1)*this.Width()-radius-1]; b_h_data[XY]=b_h_data[(Y+1)*this.Width()-radius-1]; } } //--- Blur vertically (along the Y axis) the image already blurred horizontally int dxdy=0; //--- Loop by the image height for(int X=0;X<this.Width();X++) { //--- Loop by the image width for(uint Y=radius;Y<this.Height()-radius;Y++) { XY=Y*this.Width()+X; a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0; coef=0; //--- Multiply each color component by the weight ratio corresponding to the current image pixel for(int i=-1*j;i<j+1;i=i+1) { dxdy=i*(int)this.Width(); a_temp+=a_h_data[XY+dxdy]*weights[coef]; r_temp+=r_h_data[XY+dxdy]*weights[coef]; g_temp+=g_h_data[XY+dxdy]*weights[coef]; b_temp+=b_h_data[XY+dxdy]*weights[coef]; coef++; } //--- Save each rounded color component calculated according to the ratios to the component arrays a_v_data[XY]=(uchar)::round(a_temp); r_v_data[XY]=(uchar)::round(r_temp); g_v_data[XY]=(uchar)::round(g_temp); b_v_data[XY]=(uchar)::round(b_temp); } //--- Remove blur artifacts at the top by copying adjacent pixels for(uint y=0;y<radius;y++) { XY=y*this.Width()+X; a_v_data[XY]=a_v_data[X+radius*this.Width()]; r_v_data[XY]=r_v_data[X+radius*this.Width()]; g_v_data[XY]=g_v_data[X+radius*this.Width()]; b_v_data[XY]=b_v_data[X+radius*this.Width()]; } //--- Remove blur artifacts at the bottom by copying adjacent pixels for(int y=int(this.Height()-radius);y<this.Height();y++) { XY=y*this.Width()+X; a_v_data[XY]=a_v_data[X+(this.Height()-1-radius)*this.Width()]; r_v_data[XY]=r_v_data[X+(this.Height()-1-radius)*this.Width()]; g_v_data[XY]=g_v_data[X+(this.Height()-1-radius)*this.Width()]; b_v_data[XY]=b_v_data[X+(this.Height()-1-radius)*this.Width()]; } } //--- Set the twice blurred (horizontally and vertically) image pixels to the graphical resource data array for(int i=0;i<size;i++) this.m_duplicate_res[i]=ARGB(a_v_data[i],r_v_data[i],g_v_data[i],b_v_data[i]); //--- Display the image pixels on the canvas in a loop by the image height and width from the graphical resource data array for(int X=0;X<this.Width();X++) { for(uint Y=radius;Y<this.Height()-radius;Y++) { XY=Y*this.Width()+X; this.m_canvas.PixelSet(X,Y,this.m_duplicate_res[XY]); } } //--- Done return true; } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Objects\Graph\Animations\Frame.mqhのアニメーションフレームオブジェクトクラスを改善しましょう。
クラスのprotectedセクションで、座標値を書き込み、前の輪郭長方形をシフトして後で使用するためのメソッドを宣言し、画像の下の背景を保存および復元するための仮想メソッドを記述します。
//+------------------------------------------------------------------+ //| Single animation frame class | //+------------------------------------------------------------------+ class CFrame : public CPixelCopier { protected: ENUM_ANIMATION_FRAME_TYPE m_frame_figure_type; // Type of the figure drawn by the frame ENUM_FRAME_ANCHOR m_anchor_last; // Last frame anchor point double m_x_last; // X coordinate of the upper left corner of the last frame double m_y_last; // Y coordinate of the upper left corner of the last frame int m_shift_x_prev; // Offset of the X coordinate of the last frame upper left corner int m_shift_y_prev; // Offset of the Y coordinate of the last frame upper left corner //--- Set the coordinates and offset of the outlining rectangle as the previous ones void SetLastParams(const double quad_x,const double quad_y,const int shift_x,const int shift_y,const ENUM_FRAME_ANCHOR anchor=FRAME_ANCHOR_LEFT_TOP); //--- Save and restore the background under the image virtual bool SaveRestoreBG(void) { return false; } public:
これらのメソッドはすべて、前の記事で作成したクラスの形状描画メソッドのコードを最適化した結果です。
ここで仮想メソッドはfalseを返すだけであり、子孫クラスでの実装が必要です。継承されたすべてのクラスでの実装が同じであることが判明した場合、メソッドは非仮想になり、このクラスでのみ実装されます。SetLastParams()メソッドは少し後で検討されます。
クラスのpublicセクションに、ピクセル配列をリセットするメソッドを記述します。
public: //--- Reset the pixel array void ResetArray(void) { ::ArrayResize(this.m_array,0); } //--- Return the last (1) anchor point, (2) X and (3) Y coordinate, //--- previous offset by (4) X and (5) Y, (6) type of the figure drawn by the frame ENUM_FRAME_ANCHOR LastAnchor(void) const { return this.m_anchor_last; } double LastX(void) const { return this.m_x_last; } double LastY(void) const { return this.m_y_last; } int LastShiftX(void) const { return this.m_shift_x_prev; } int LastShiftY(void) const { return this.m_shift_y_prev; } ENUM_ANIMATION_FRAME_TYPE FrameFigureType(void) const { return this.m_frame_figure_type; } //--- Default constructor CFrame(); protected:
このメソッドはピクセル配列のサイズをゼロにするだけです。その後の復元のために背景を保存するメソッドがまず配列サイズを確認して、輪郭を描く長方形のサイズの変更を正しく処理できるようになります。ゼロの場合、背景が保存されます。それ以外の場合、背景は以前に正しい座標と保存された領域サイズで保存されたと見なされます。したがって、描画された形状を変更したら、配列をリセットする必要があります。それ以外の場合、新しい画像の下の背景は保存されず、別の領域からの完全に異なる背景が後で復元されます(サイズ、座標、描画された形状の外観が変更される前に保存されたもの)。
クラスのprotectedセクションで、今日作成してテストする幾何学的図形のアニメーションフレームのクラスコンストラクタを宣言します。
protected: //--- Text frame constructor CFrame(const int id, const int x, const int y, const string text, CGCnvElement *element); //--- Rectangular frame constructor CFrame(const int id, const int x, const int y, const int w, const int h, CGCnvElement *element); //--- Geometric frame constructor CFrame(const int id, const int x, const int y, const int len, CGCnvElement *element); }; //+------------------------------------------------------------------+
以前に作成した継承クラスと同様に、オブジェクトID、左上のフレーム角度のX座標とY座標、正方形のフレーム辺の長さ、および新しいオブジェクトが作成されるグラフィク要素へのポインタをクラスコンストラクタに渡します。
以下は、幾何学的アニメーションフレームオブジェクトのコンストラクタの実装です。
//+------------------------------------------------------------------+ //| Geometric frame constructor | //+------------------------------------------------------------------+ CFrame::CFrame(const int id,const int x,const int y,const int len,CGCnvElement *element) : CPixelCopier(id,x,y,len,len,element) { this.m_frame_figure_type=ANIMATION_FRAME_TYPE_GEOMETRY; this.m_anchor_last=FRAME_ANCHOR_LEFT_TOP; this.m_x_last=x; this.m_y_last=y; this.m_shift_x_prev=0; this.m_shift_y_prev=0; } //+------------------------------------------------------------------+
初期化リストで、必要なすべてのパラメータを親クラスコンストラクタに渡します。クラス本体で、現在の記事でアニメーションフレーム型のリストに追加した形状タイプをANIMATION_FRAME_TYPE_GEOMETRYとして設定します。他のパラメータは、以前に検討されたテキストおよび長方形のアニメーションクラスのコンストラクタと同様に初期化されます。
以下は、輪郭を描く長方形の座標とオフセットを前のものと同じように設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the coordinates and the offset | //| of the outlining rectangle as the previous ones | //+------------------------------------------------------------------+ void CFrame::SetLastParams(const double quad_x,const double quad_y,const int shift_x,const int shift_y,const ENUM_FRAME_ANCHOR anchor=FRAME_ANCHOR_LEFT_TOP) { this.m_anchor_last=anchor; this.m_x_last=quad_x; this.m_y_last=quad_y; this.m_shift_x_prev=shift_x; this.m_shift_y_prev=shift_y; } //+------------------------------------------------------------------+
フォームの背景を保存および復元しながら、以前に検討された絶えず繰り返されるコードが、図形の描画メソッドからこのメソッドに移動されました。
CFrameクラスの子孫クラスを改善しましょう。
\MQL5\Include\DoEasy\Objects\Graph\Animations\FrameQuad.mqh長方形アニメーションクラスファイルを開き、必要な変更を加えます。
クラスのprivateセクションで、輪郭を描く長方形の座標のオフセットを格納するための2つの変数を宣言し、画像の下に背景を保存および復元するための仮想メソッドを宣言します。
//+------------------------------------------------------------------+ //| Class of a single rectangular animation frame | //+------------------------------------------------------------------+ class CFrameQuad : public CFrame { private: double m_quad_x; // X coordinate of the rectangle enclosing the shape double m_quad_y; // Y coordinate of the rectangle enclosing the shape uint m_quad_width; // Width of the rectangle enclosing the shape uint m_quad_height; // Height of the rectangle enclosing the shape int m_shift_x; // Offset of the X coordinate of the rectangle enclosing the shape int m_shift_y; // Offset of the Y coordinate of the rectangle enclosing the shape //--- Save and restore the background under the image virtual bool SaveRestoreBG(void); public:
クラスのpublicセクションで、パラメトリックコンストラクタの実装を補足します。次に、本体ですべてのクラス変数が初期化されます(以前は初期化されていなかったため、正しくありませんでした)。
public: //--- Constructors CFrameQuad() {;} CFrameQuad(const int id,CGCnvElement *element) : CFrame(id,0,0,0,0,element) { this.m_anchor_last=FRAME_ANCHOR_LEFT_TOP; this.m_quad_x=0; this.m_quad_y=0; this.m_quad_width=0; this.m_quad_height=0; this.m_shift_x=0; this.m_shift_y=0; }
点を描画するメソッド例として、背景を保存/復元する描画メソッドを見てみましょう。
//+------------------------------------------------------------------+ //| Set the color of the dot with the specified coordinates | //+------------------------------------------------------------------+ bool CFrameQuad::SetPixelOnBG(const int x,const int y,const color clr,const uchar opacity=255,const bool redraw=false) { //--- Set the coordinates of the outlining rectangle this.m_quad_x=x; this.m_quad_y=y; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) this.m_quad_width=1; this.m_quad_height=1; //--- Calculate coordinate offsets for the saved area depending on the anchor point int shift_x=0,shift_y=0; this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y); //--- If the pixel array is not empty, the background under the image has already been saved - //--- restore the previously saved background (by the previous coordinates and offsets) if(::ArraySize(this.m_array)>0) { if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev))) return false; } //--- If a background area with calculated coordinates and size under the future image is successfully saved if(!CPixelCopier::CopyImgDataToArray(int(this.m_quad_x+shift_x),int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height)) return false; //--- Draw the shape and update the element this.m_element.SetPixel(x,y,clr,opacity); this.m_element.Update(redraw); this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP; this.m_x_last=this.m_quad_x; this.m_y_last=this.m_quad_y; this.m_shift_x_prev=shift_x; this.m_shift_y_prev=shift_y; return true; } //+------------------------------------------------------------------+
次に、強調表示されたコードセグメントを新しく作成されたメソッドに置き換えることができます。現在メソッドは以下のようになります。
//+------------------------------------------------------------------+ //| Set the color of the dot with the specified coordinates | //+------------------------------------------------------------------+ bool CFrameQuad::SetPixelOnBG(const int x,const int y,const color clr,const uchar opacity=255,const bool redraw=false) { //--- Set the coordinates of the outlining rectangle this.m_quad_x=x; this.m_quad_y=y; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) this.m_quad_width=1; this.m_quad_height=1; //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw the shape and update the element this.m_element.SetPixel(x,y,clr,opacity); this.SetLastParams(this.m_quad_x,this.m_quad_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+
ご覧のとおり、指定したコードセグメントを新しいメソッドの呼び出しに置き換えると、コードが大幅に短縮され、読みやすくなります。背景を保存および復元して図形を描画するすべてのメソッドを、同じように変更しました。このようなメソッドは多数あり、同様の変更が行われているため、ここでそれらすべてを検討しても意味がありません。以下の添付ファイルでご覧ください。
楕円を描画するメソッドについてのみ説明します。覚えていらっしゃるかもしれませんが、CCanvasには潜在的なゼロによる除算があるため、前回の記事では楕円を描画しませんでした。これは、メソッドが、楕円が描かれている長方形の同様のx1とx2、またはy1とy2の座標を受け取った場合に発生します。したがって、同じ座標の値が等しい場合は調整する必要があります。
//+------------------------------------------------------------------+ //| Draw an ellipse using two points while applying | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawEllipseAAOnBG(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 bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { //--- Get the minimum and maximum coordinates double xn1=::fmin(x1,x2); double xn2=::fmax(x1,x2); double yn1=::fmin(y1,y2); double yn2=::fmax(y1,y2); if(xn2==xn1) xn2=xn1+0.1; if(yn2==yn1) yn2=yn1+0.1; //--- Set the coordinates of the outlining rectangle this.m_quad_x=xn1-1; this.m_quad_y=yn1-1; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) this.m_quad_width=int(::ceil((xn2-xn1)+1))+2; this.m_quad_height=int(::ceil((yn2-yn1)+1))+2; //--- Adjust the width and height of the outlining rectangle if(this.m_quad_width<3) this.m_quad_width=3; if(this.m_quad_height<3) this.m_quad_height=3; //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw the shape and update the element this.m_element.DrawEllipseAA(xn1,yn1,xn2,yn2,clr,opacity,style); this.SetLastParams(this.m_quad_x,this.m_quad_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Draw an ellipse using two points while applying | //| Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawEllipseWuOnBG(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 bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { //--- Get the minimum and maximum coordinates double xn1=::fmin(x1,x2); double xn2=::fmax(x1,x2); double yn1=::fmin(y1,y2); double yn2=::fmax(y1,y2); if(xn2==xn1) xn2=xn1+0.1; if(yn2==yn1) yn2=yn1+0.1; //--- Set the coordinates of the outlining rectangle this.m_quad_x=xn1-1; this.m_quad_y=yn1-1; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) this.m_quad_width=int(::ceil((xn2-xn1)+1))+2; this.m_quad_height=int(::ceil((yn2-yn1)+1))+2; //--- Adjust the width and height of the outlining rectangle if(this.m_quad_width<3) this.m_quad_width=3; if(this.m_quad_height<3) this.m_quad_height=3; //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw the shape and update the element this.m_element.DrawEllipseWu((int)xn1,(int)yn1,(int)xn2,(int)yn2,clr,opacity,style); this.SetLastParams(this.m_quad_x,this.m_quad_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+
以下は、画像の下の背景を保存および復元するメソッドです。
//+------------------------------------------------------------------+ //| Save and restore the background under the image | //+------------------------------------------------------------------+ bool CFrameQuad::SaveRestoreBG(void) { //--- Calculate coordinate offsets for the saved area depending on the anchor point this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,FRAME_ANCHOR_LEFT_TOP,this.m_shift_x,this.m_shift_y); //--- If the pixel array is not empty, the background under the image has already been saved - //--- restore the previously saved background (by the previous coordinates and offsets) if(::ArraySize(this.m_array)>0) { if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev))) return false; } //--- Return the result of saving the background area with the calculated coordinates and size under the future image return CPixelCopier::CopyImgDataToArray(int(this.m_quad_x+this.m_shift_x),int(this.m_quad_y+this.m_shift_y),this.m_quad_width,this.m_quad_height); } //+------------------------------------------------------------------+
背景を保存および復元して図形を描画するためのメソッドから絶えず繰り返されるコードブロックは、単にメソッドに移動されました。
\MQL5\Include\DoEasy\Objects\Graph\Animations\FrameText.mqhの変更は最小限です。2つのコードセグメントで 文字列「ENUM_TEXT_ANCHOR」を「ENUM_FRAME_ANCHOR」で置き換えるだけです。
//+------------------------------------------------------------------+ //| Single text animation frame class | //+------------------------------------------------------------------+ class CFrameText : public CFrame { private: public: //--- Display the text on the background while saving and restoring the background bool TextOnBG(const string text,const int x,const int y,const ENUM_FRAME_ANCHOR anchor,const color clr,const uchar opacity,bool redraw=false); //--- Constructors CFrameText() {;} CFrameText(const int id,CGCnvElement *element) : CFrame(id,0,0,"",element) {} }; //+------------------------------------------------------------------+ //| Display the text on the background, while saving and restoring the background | //+------------------------------------------------------------------+ bool CFrameText::TextOnBG(const string text,const int x,const int y,const ENUM_FRAME_ANCHOR anchor,const color clr,const uchar opacity,bool redraw=false) {
幾何学的アニメーションフレームオブジェクトクラス
幾何学的アニメーションフレームオブジェクトクラスの背後にあるロジックは、その2つの前身であるテキストオブジェクトと長方形アニメーションと非常によく似ています。多角形の頂点の数に応じて、円上の多角形の頂点の座標を計算するメソッドを作成するだけで済みます。
正多角形のデカルト座標の方程式:
xcとycを中心座標、Rを正多角形に外接する円の半径、ϕ0を中心に対する最初の頂点の座標(角度)とします。この場合、正多角形の頂点のデカルト座標は、次の方程式によって決定されます。
ここで i の値の範囲は 0 ~n−1です。
\MQL5\Include\DoEasy\Objects\Graph\Animations\で、CFrameGeometryクラスの新しいFrameGeometry.mqhファイルを作成します。
ファイルはアニメーションフレームオブジェクトクラスファイルをインクルードします。また、クラスはこのクラスから継承される必要があります。
//+------------------------------------------------------------------+ //| FrameGeometry.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Frame.mqh" //+------------------------------------------------------------------+ //| Class of a single rectangular animation frame | //+------------------------------------------------------------------+ class CFrameGeometry : public CFrame { }
クラス本体の定義全体を検討してください。privateでは、すべてのクラス変数と、画像の背景を保存および復元するための仮想メソッドがセクションで宣言されています(長方形のアニメーションオブジェクトのコンテキストで上記のメソッドを検討しました。クラスでは、前の記事で検討した形状描画メソッドから繰り返しコードブロックを転送するだけです。)privateセクションでは、正多角形の座標を計算するメソッドも宣言されています。
クラスのpublicセクションには、コンストラクタ(デフォルトおよびパラメトリック)と、正多角形を描画するメソッド(単純、塗りつぶし、および平滑化を使用したもの)があります。
//+------------------------------------------------------------------+ //| Class of a single rectangular animation frame | //+------------------------------------------------------------------+ class CFrameGeometry : public CFrame { private: double m_square_x; // X coordinate of the square enclosing the shape double m_square_y; // Y coordinate of the square enclosing the shape uint m_square_length; // Length of the sides of the square enclosing the shape int m_shift_x; // Offset of the X coordinate of the square enclosing the shape int m_shift_y; // Offset of the Y coordinate of the square enclosing the shape int m_array_x[]; // Array of shape X coordinates int m_array_y[]; // Array of shape Y coordinates //--- Save and restore the background under the image virtual bool SaveRestoreBG(void); //--- Calculate coordinates of the regular polygon built in a circumscribed circle inscribed in a square void CoordsNgon(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left square angle the circle will be inscribed into const int coord_y, // Y coordinate of the upper-left square angle whose inscribed circle is used to build a polygon const int len, // Square sides length const double angle); // Polygon rotation angle (the polygon is built from the point 0 to the right of the circle center) public: //--- Constructors CFrameGeometry() {;} CFrameGeometry(const int id,CGCnvElement *element) : CFrame(id,0,0,0,0,element) { ::ArrayResize(this.m_array_x,0); ::ArrayResize(this.m_array_y,0); this.m_anchor_last=FRAME_ANCHOR_LEFT_TOP; this.m_square_x=0; this.m_square_y=0; this.m_square_length=0; this.m_shift_x=0; this.m_shift_y=0; } //--- Destructor ~CFrameGeometry() { ::ArrayFree(this.m_array_x); ::ArrayFree(this.m_array_y); } //+------------------------------------------------------------------+ //| Methods of drawing regular polygons | //+------------------------------------------------------------------+ //--- Draw a regular polygon without smoothing bool DrawNgonOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a regular filled polygon bool DrawNgonFillOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a regular polygon using AntiAliasing algorithm bool DrawNgonAAOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX); // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value //--- Draw a regular polygon using Wu algorithm bool DrawNgonWuOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX); // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value //--- Draw a regular 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. bool DrawNgonSmoothOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle 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 bool redraw=false, // Chart redraw flag const ENUM_LINE_STYLE style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND);// Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a regular polygon having a specified width using smoothing algorithm with the preliminary filtration bool DrawNgonThickOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // line style ENUM_LINE_END end_style=LINE_END_ROUND); // line ends style }; //+------------------------------------------------------------------+
クラスメソッドの実装をいくつか見てみましょう。
以下は、正多角形を描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw a regular polygon | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonOnBG(const int N, const int coord_x, const int coord_y, const int len, const double angle, const color clr, const uchar opacity=255, const bool redraw=false) { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygon(this.m_array_x,this.m_array_y,clr,opacity); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+
以前のクラス(長方形のアニメーションフレームクラス)の同様の多角形描画メソッドとの唯一の違いは、以前に準備された多角形頂点座標の配列がここに渡されないことです。代わりに、このメソッドは、多角形の頂点の数と、多角形が描画される正方形のフレームの左上の角度の座標を受け取ります。このメソッドでは頂点の数、座標、円の半径、回転角によって多角形の頂点座標を計算するメソッドと、XおよびY頂点座標配列を入力するメソッドが呼び出されます。次に、メソッドに対応する多角形は、CCanvasクラスを使用して単純に描画されます。
比較のために、塗りつぶされた多角形を描画するメソッドを見てみましょう。
//+------------------------------------------------------------------+ //| Draw a regular filled polygon | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonFillOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonFill(this.m_array_x,this.m_array_y,clr,opacity); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+
最初のメソッドとの違いは、塗りつぶされた多角形を描画するメソッドを呼び出すことにあります。
残りのメソッドは、特定の線幅で多角形を描画するために輪郭を描く長方形の座標を計算するいくつかの特殊性を除いて、上記の2つとほぼ同じです。輪郭を描く長方形の座標とサイズを計算するときは、描画された線の幅を考慮する必要があります。
以下は、残りの正多角形描画メソッドです。
//+------------------------------------------------------------------+ //| Draw a regular filled polygon | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonFillOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonFill(this.m_array_x,this.m_array_y,clr,opacity); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+ //| Draw a regular polygon using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonAAOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonAA(this.m_array_x,this.m_array_y,clr,opacity,style); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+ //| Draw a regular polygon using | //| Wu algorithm | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonWuOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonWu(this.m_array_x,this.m_array_y,clr,opacity,style); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+ //| Draw a regular polygon of a specified width | //| using two smoothing algorithms in series. | //| First, individual segments are smoothed based on Bezier curves. | //| Then, to improve the rendering quality, | //| a raster smoothing algorithm is applied | //| made of these segments. | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonSmoothOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle 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 bool redraw=false, // Chart redraw flag const ENUM_LINE_STYLE style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration's values { //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-1; this.m_square_y=coord_y-1; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonSmooth(this.m_array_x,this.m_array_y,size,clr,opacity,tension,step,style,end_style); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+ //| Draw a regular polygon with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ bool CFrameGeometry::DrawNgonThickOnBG(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // line style ENUM_LINE_END end_style=LINE_END_ROUND) // line ends style { //--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size int correct=int(::ceil((double)size/2.0))+1; //--- Set the coordinates of the outlining rectangle this.m_square_x=coord_x-correct; this.m_square_y=coord_y-correct; //--- Set the width and height of a square frame (to be used as the size of the saved area) this.m_square_length=len+correct*2; //--- Calculate the polygon coordinates on the circle this.CoordsNgon(N,coord_x,coord_y,len,angle); //--- Restore the previously saved background and save the new one if(!this.SaveRestoreBG()) return false; //--- Draw a polygon inscribed in a circle and update the element this.m_element.DrawPolygonThick(this.m_array_x,this.m_array_y,size,clr,opacity,style,end_style); this.SetLastParams(this.m_square_x,this.m_square_y,this.m_shift_x,this.m_shift_y); this.m_element.Update(redraw); return true; } //+------------------------------------------------------------------+
以下は、画像の下の背景を保存および復元する仮想メソッドです。
//+------------------------------------------------------------------+ //| Save and restore the background under the image | //+------------------------------------------------------------------+ bool CFrameGeometry::SaveRestoreBG(void) { //--- Calculate coordinate offsets for the saved area depending on the anchor point this.m_element.GetShiftXYbySize(this.m_square_length,this.m_square_length,FRAME_ANCHOR_LEFT_TOP,this.m_shift_x,this.m_shift_y); //--- If the pixel array is not empty, the background under the image has already been saved - //--- restore the previously saved background (by the previous coordinates and offsets) if(::ArraySize(this.m_array)>0) { if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev))) return false; } //--- Return the result of saving the background area with the calculated coordinates and size under the future image return CPixelCopier::CopyImgDataToArray(int(this.m_square_x+this.m_shift_x),int(this.m_square_y+this.m_shift_y),this.m_square_length,this.m_square_length); } //+------------------------------------------------------------------+
これは、前の記事の図形を描画するためのメソッドから移動された繰り返しコードブロックです。
以下は、円に内接する正多角形の座標を計算するメソッドです。
//+------------------------------------------------------------------+ //| Calculate the coordinates of the regular polygon | //+------------------------------------------------------------------+ void CFrameGeometry::CoordsNgon(const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left square angle the circle will be inscribed into const int coord_y, // Y coordinate of the upper-left square angle whose inscribed circle is used to build a polygon const int len, // Length of the sides of the square a polygon is to be inscribed into const double angle) // Polygon rotation angle (the polygon is built from the point 0 to the right of the circle center) { //--- If there are less than three sides, there will be three int n=(N<3 ? 3 : N); //--- Set the size of coordinate arrays according to the number of vertices ::ArrayResize(this.m_array_x,n); ::ArrayResize(this.m_array_y,n); //--- Calculate the radius of the circumscribed circle double R=(double)len/2.0; //--- X and Y coordinates of the circle center double xc=coord_x+R; double yc=coord_y+R; //--- Calculate the polygon inclination angle in degrees double grad=angle*M_PI/180.0; //--- In the loop by the number of vertices, calculate the coordinates of each next polygon vertex for(int i=0; i<n; i++) { //--- Angle of the current polygon vertex with the rotation in degrees double a=2.0*M_PI*i/n+grad; //--- X and Y coordinates of the current polygon vertex double xi=xc+R*::cos(a); double yi=yc+R*::sin(a); //--- Set the current coordinates to the arrays this.m_array_x[i]=int(::floor(xi)); this.m_array_y[i]=int(::floor(yi)); } } //+------------------------------------------------------------------+
メソッドのロジックについては、コードコメントで詳しく説明されています。以下は、多角形のデカルト座標を計算するための方程式です。
ご自分でお勉強ください。ここではすべてが明確であると思います。ご質問がある場合は、下のコメント欄でお気軽にお問い合わせください。
幾何学的アニメーションフレームオブジェクトのクラスの準備ができました。
次に、外部プログラムからアクセスできるようにし、このクラスのオブジェクトをすばやく作成できるようにする必要があります。
新しく作成されたすべてのアニメーションフレームオブジェクトは、CAnimationsクラスの独自のリストに保存されます。
\MQL5\Include\DoEasy\Objects\Graph\Animations\Animations.mqhクラスファイルに必要な改善を加えます。
新しく作成された幾何学的アニメーションフレームオブジェクトクラスのファイルをクラスファイルにインクルードして、privateセクションで、新しく作成されたすべてのクラスオブジェクトを格納するリストを宣言します。
//+------------------------------------------------------------------+ //| Animations.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "FrameText.mqh" #include "FrameQuad.mqh" #include "FrameGeometry.mqh" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CAnimations : public CObject { private: CGCnvElement *m_element; // Pointer to the graphical element CArrayObj m_list_frames_text; // List of text animation frames CArrayObj m_list_frames_quad; // List of rectangular animation frames CArrayObj m_list_frames_geom; // List of geometric shape animations frames //--- Return the flag indicating the presence of the frame object with the specified ID in the list bool IsPresentFrame(const ENUM_ANIMATION_FRAME_TYPE frame_type,const int id); //--- Return or create a new animation frame object CFrame *GetOrCreateFrame(const string source,const int id,const ENUM_ANIMATION_FRAME_TYPE frame_type,const bool create_new); public:
クラスのpublicセクションで、幾何学的アニメーションフレームの新しいオブジェクトを作成するためのメソッドを宣言し、これらのオブジェクトのリストへのポインタを返すメソッドを記述します。
public: CAnimations(CGCnvElement *element); CAnimations(){;} //--- Create a new (1) rectangular, (2) text and geometric animation frame object CFrame *CreateNewFrameText(const int id); CFrame *CreateNewFrameQuad(const int id); CFrame *CreateNewFrameGeometry(const int id); //--- Return the animation frame objects by ID CFrame *GetFrame(const ENUM_ANIMATION_FRAME_TYPE frame_type,const int id); //--- Return the list of (1) text, (2) rectangular and (3) geometric shape animation frames CArrayObj *GetListFramesText(void) { return &this.m_list_frames_text; } CArrayObj *GetListFramesQuad(void) { return &this.m_list_frames_quad; } CArrayObj *GetListFramesGeometry(void) { return &this.m_list_frames_geom; }
次に、正多角形を描画するメソッドを宣言します。
//+------------------------------------------------------------------+ //| Methods of drawing regular polygons | //+------------------------------------------------------------------+ //--- Draw a regular polygon without smoothing bool DrawNgonOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a regular filled polygon bool DrawNgonFillOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a regular polygon using AntiAliasing algorithm bool DrawNgonAAOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX); // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value //--- Draw a regular polygon using Wu algorithm bool DrawNgonWuOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX); // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value //--- Draw a regular 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. bool DrawNgonSmoothOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle 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 bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const ENUM_LINE_STYLE style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND);// Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a regular polygon having a specified width using smoothing algorithm with the preliminary filtration bool DrawNgonThickOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // line style ENUM_LINE_END end_style=LINE_END_ROUND); // line ends style }; //+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+
クラス内の「ENUM_TEXT_ANCHOR」はすべて「ENUM_FRAME_ANCHOR」に置き換える必要があります。
タイプとIDでアニメーションフレームオブジェクトを返すメソッドに新しい型のアニメーションフレームオブジェクトの処理を追加します。
//+------------------------------------------------------------------+ //| Return the animation frame objects by type and ID | //+------------------------------------------------------------------+ CFrame *CAnimations::GetFrame(const ENUM_ANIMATION_FRAME_TYPE frame_type,const int id) { //--- Declare the pointer to the animation frame object CFrame *frame=NULL; //--- Depending on the necessary object type, receive their number in the appropriate list int total= ( frame_type==ANIMATION_FRAME_TYPE_TEXT ? this.m_list_frames_text.Total() : frame_type==ANIMATION_FRAME_TYPE_QUAD ? this.m_list_frames_quad.Total() : frame_type==ANIMATION_FRAME_TYPE_GEOMETRY ? this.m_list_frames_geom.Total() : 0 ); //--- Get the next object in the loop ... for(int i=0;i<total;i++) { //--- ... by the list corresponding to the animation frame type switch(frame_type) { case ANIMATION_FRAME_TYPE_TEXT : frame=this.m_list_frames_text.At(i); break; case ANIMATION_FRAME_TYPE_QUAD : frame=this.m_list_frames_quad.At(i); break; case ANIMATION_FRAME_TYPE_GEOMETRY : frame=this.m_list_frames_geom.At(i); break; default: break; } //--- if failed to get the pointer, move on to the next one if(frame==NULL) continue; //--- If the object ID correspond to the required one, //--- return the pointer to the detected object if(frame.ID()==id) return frame; } //--- Nothing is found - return NULL return NULL; } //+------------------------------------------------------------------+
以下は、新しい幾何学的アニメーションフレームオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a new geometric animation frame object | //+------------------------------------------------------------------+ CFrame *CAnimations::CreateNewFrameGeometry(const int id) { //--- If the object with such an ID is already present, inform of that in the journal and return NULL if(this.IsPresentFrame(ANIMATION_FRAME_TYPE_GEOMETRY,id)) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST),(string)id); return NULL; } //--- Create a new geometric animation frame object with the specified ID CFrame *frame=new CFrameGeometry(id,this.m_element); //--- If failed to create an object, inform of that and return NULL if(frame==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_FRAME)); return NULL; } //--- If failed to add the created object to the list, inform of that, remove the object and return NULL if(!this.m_list_frames_geom.Add(frame)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST)," ID: ",id); delete frame; return NULL; } //--- Return the pointer to a newly created object return frame; } //+------------------------------------------------------------------+
メソッドのロジックは、コードのコメントで完全に説明されています。
アニメーションフレームオブジェクトを返す/作成するメソッドに新しい型のアニメーションフレームオブジェクトの処理を追加します。
//+------------------------------------------------------------------+ //| Return or create a new animation frame object | //+------------------------------------------------------------------+ CFrame *CAnimations::GetOrCreateFrame(const string source,const int id,const ENUM_ANIMATION_FRAME_TYPE frame_type,const bool create_new) { //--- Declare null pointers to objects CFrameQuad *frame_q=NULL; CFrameText *frame_t=NULL; CFrameGeometry *frame_g=NULL; //--- Depending on the required object type switch(frame_type) { //--- If this is a text animation frame, case ANIMATION_FRAME_TYPE_TEXT : //--- get the pointer to an object with a specified ID frame_t=this.GetFrame(ANIMATION_FRAME_TYPE_TEXT,id); //--- If the pointer is obtained, return it if(frame_t!=NULL) return frame_t; //--- If the flag of creating a new object is not set, report an error and return NULL if(!create_new) { ::Print(source,CMessage::Text(MSG_FORM_OBJECT_FRAME_NOT_EXIST_LIST),(string)id); return NULL; } //--- Return the result of creating a new text animation frame object (pointer to the created object) return this.CreateNewFrameText(id); //--- If this is a rectangular animation frame case ANIMATION_FRAME_TYPE_QUAD : //--- get the pointer to an object with a specified ID frame_q=this.GetFrame(ANIMATION_FRAME_TYPE_QUAD,id); //--- If the pointer is obtained, return it if(frame_q!=NULL) return frame_q; //--- If the flag of creating a new object is not set, report an error and return NULL if(!create_new) { ::Print(source,CMessage::Text(MSG_FORM_OBJECT_FRAME_NOT_EXIST_LIST),(string)id); return NULL; } //--- Return the result of creating a new rectangular animation frame object (pointer to the created object) return this.CreateNewFrameQuad(id); //--- If this is a geometric animation frame case ANIMATION_FRAME_TYPE_GEOMETRY : //--- get the pointer to an object with a specified ID frame_g=this.GetFrame(ANIMATION_FRAME_TYPE_GEOMETRY,id); //--- If the pointer is obtained, return it if(frame_g!=NULL) return frame_g; //--- If the flag of creating a new object is not set, report an error and return NULL if(!create_new) { ::Print(source,CMessage::Text(MSG_FORM_OBJECT_FRAME_NOT_EXIST_LIST),(string)id); return NULL; } //--- Return the result of creating a new geometric animation frame object (pointer to the created object) return this.CreateNewFrameGeometry(id); //--- In the remaining cases, return NULL default: return NULL; } } //+------------------------------------------------------------------+
いつものように、ここでのロジック全体はコードコメントで説明されています。
クラスの最後に正多角形を描画するメソッドを実装します。
//+------------------------------------------------------------------+ //| Draw a regular polygon without smoothing | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonOnBG(N,coord_x,coord_y,len,angle,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a regular filled polygon | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonFillOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonFillOnBG(N,coord_x,coord_y,len,angle,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a regular polygon using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonAAOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonAAOnBG(N,coord_x,coord_y,len,angle,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a regular polygon using | //| Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonWuOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonWuOnBG(N,coord_x,coord_y,len,angle,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a regular polygon of a specified width | //| using two smoothing algorithms in series. | //| First, individual segments are smoothed based on Bezier curves. | //| Then, to improve the rendering quality, | //| a raster smoothing algorithm is applied | //| to the polygon made of these segments. | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonSmoothOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle 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 bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const ENUM_LINE_STYLE style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration's values { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonSmoothOnBG(N,coord_x,coord_y,len,angle,size,clr,opacity,tension,step,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a regular polygon with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ bool CAnimations::DrawNgonThickOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // line style ENUM_LINE_END end_style=LINE_END_ROUND) // line ends style { CFrameGeometry *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_GEOMETRY,create_new); if(frame==NULL) return false; return frame.DrawNgonThickOnBG(N,coord_x,coord_y,len,angle,size,clr,opacity,redraw,style,end_style); } //+------------------------------------------------------------------+
これらすべてのメソッドのロジックは完全に同じなので、例として最後のメソッドを使用しましょう。
ご覧のとおり、ここではすべてが簡単です。まず、リストから既製の幾何学的アニメーションフレームオブジェクトを取得するか、リストにない場合は作成します。新しいオブジェクト作成フラグが有効になっていて、オブジェクトの取得または作成に失敗した場合は、falseを返します。
それ以外の場合は、リストから受信した、または最初から作成したh幾何学的アニメーションフレームオブジェクトクラスの同名メソッドを呼び出した結果を返します 。
次に、\MQL5\Include\DoEasy\Objects\Graph\Form.mqhのフォームオブジェクトクラスを改善しましょう。
クラス内の「ENUM_TEXT_ANCHOR」はすべて「ENUM_FRAME_ANCHOR」に置き換えます。
クラスのprivateセクションで、3つのアニメーションフレームクラスのピクセル配列のサイズをリセットするためのメソッドを宣言します。
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { private: CArrayObj m_list_elements; // List of attached elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object color m_color_frame; // Form frame color int m_frame_width_left; // Form frame width to the left int m_frame_width_right; // Form frame width to the right int m_frame_width_top; // Form frame width at the top int m_frame_width_bottom; // Form frame width at the bottom //--- Initialize the variables void Initialize(void); //--- Reset the array size of (1) text, (2) rectangular and (3) geometric animation frames void ResetArrayFrameT(void); void ResetArrayFrameQ(void); void ResetArrayFrameG(void); //--- Return the name of the dependent object
これは、図形が描画されるフォームの背景を保存および復元する方法を正しく操作するために必要です(これについては上記で説明しました)。
クラスのpublicセクションで、フォームの外観をキャプチャするためのメソッドを記述し、配列からグラフィカルリソースを復元する仮想メソッドを宣言します。
//--- Draw an embossed (concave) field void DrawFieldStamp(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Field width const int height, // Field height const color colour, // Field color const uchar opacity); // Field opacity //--- Capture the appearance of the created form void Done(void) { CGCnvElement::CanvasUpdate(false); CGCnvElement::ResourceStamp(DFUN); } //--- Restore the resource from the array virtual bool Reset(void); //+------------------------------------------------------------------+ //| Methods of working with image pixels | //+------------------------------------------------------------------+
フォームの外観をキャプチャするメソッドが必要なのはなぜでしょうか。
フォームを作成し、必要な変更不可能な要素をすべてフォームに描画したとします。次に、新しく作成したフォームの外観をグラフィカルリソースのコピー配列にコピーして、必要に応じて元のフォームの外観を返すことができるようにする必要があります。フォームのすべての変更は、グラフィカルリソースに正確に表示されます。フォームの再描画を回避するには、最初に作成したフォームのコピーを特別な配列に格納するだけで、いつでも最初の外観を復元できます。Reset()メソッドはまさにそれを行います。
クラスのpublicセクションで正多角形を描画するメソッドを記述します。
//--- Draw a regular polygon without smoothing bool DrawNgonOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawNgonOnBG(id,N,coord_x,coord_y,len,angle,clr,opacity,create_new,redraw) : false); } //--- Draw a regular filled polygon bool DrawNgonFillOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawNgonFillOnBG(id,N,coord_x,coord_y,len,angle,clr,opacity,create_new,redraw) : false); } //--- Draw a regular polygon using AntiAliasing algorithm bool DrawNgonAAOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { return(this.m_animations!=NULL ? this.m_animations.DrawNgonAAOnBG(id,N,coord_x,coord_y,len,angle,clr,opacity,create_new,redraw,style) : false); } //--- Draw a regular polygon using Wu algorithm bool DrawNgonWuOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value { return(this.m_animations!=NULL ? this.m_animations.DrawNgonWuOnBG(id,N,coord_x,coord_y,len,angle,clr,opacity,create_new,redraw,style) : false); } //--- Draw a regular 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. bool DrawNgonSmoothOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle 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 bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const ENUM_LINE_STYLE style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration's values { return(this.m_animations!=NULL ? this.m_animations.DrawNgonSmoothOnBG(id,N,coord_x,coord_y,len,angle,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false); } //--- Draw a regular polygon having a specified width using smoothing algorithm with the preliminary filtration bool DrawNgonThickOnBG(const int id, // Frame ID const int N, // Number of polygon vertices const int coord_x, // X coordinate of the upper-left frame angle const int coord_y, // Y coordinate of the upper-left frame angle const int len, // Frame sides length const double angle, // Polygon rotation angle const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // line style ENUM_LINE_END end_style=LINE_END_ROUND) // line ends style { return(this.m_animations!=NULL ? this.m_animations.DrawNgonThickOnBG(id,N,coord_x,coord_y,len,angle,size,clr,opacity,create_new,redraw,style,end_style) : false); }
すべてのメソッドは同一であり、上記で検討したCAnimationsクラスインスタンスの適切なメソッドを呼び出した結果を返します。
宣言されたメソッドをクラス本体の外側に実装します。
以下は、3つのアニメーションフレームオブジェクトの配列のサイズをリセットする3つのメソッドです。
//+------------------------------------------------------------------+ //| Reset the array size of the text animation frames | //+------------------------------------------------------------------+ void CForm::ResetArrayFrameT(void) { if(this.m_animations==NULL) return; CArrayObj *list=this.m_animations.GetListFramesText(); if(list==NULL) return; for(int i=0;i<list.Total();i++) { CFrameText *frame=list.At(i); if(frame==NULL) continue; frame.ResetArray(); } } //+------------------------------------------------------------------+ //| Reset the size of the rectangular animation frame array | //+------------------------------------------------------------------+ void CForm::ResetArrayFrameQ(void) { if(this.m_animations==NULL) return; CArrayObj *list=this.m_animations.GetListFramesQuad(); if(list==NULL) return; for(int i=0;i<list.Total();i++) { CFrameQuad *frame=list.At(i); if(frame==NULL) continue; frame.ResetArray(); } } //+------------------------------------------------------------------+ //| Reset the size of the geometric animation frame array | //+------------------------------------------------------------------+ void CForm::ResetArrayFrameG(void) { if(this.m_animations==NULL) return; CArrayObj *list=this.m_animations.GetListFramesGeometry(); if(list==NULL) return; for(int i=0;i<list.Total();i++) { CFrameGeometry *frame=list.At(i); if(frame==NULL) continue; frame.ResetArray(); } } //+------------------------------------------------------------------+
すべてのメソッドは同一です。
CAnimationクラスオブジェクトが存在しない場合は、メソッドを終了します。オブジェクトにはアニメーションがありません。
メソッドに対応するアニメーションフレームのリストへのポインタを取得します。取得したリストによるループで、次のアニメーションフレームオブジェクトへのポインタを取得し、そのピクセル配列をゼロにリセットします。
以下は、配列からリソースを復元するメソッドです。
//+------------------------------------------------------------------+ //| Restore the resource from the array | //+------------------------------------------------------------------+ bool CForm::Reset(void) { CGCnvElement::Reset(); this.ResetArrayFrameQ(); this.ResetArrayFrameT(); this.ResetArrayFrameG(); return true; } //+------------------------------------------------------------------+
まず、コピー配列からグラフィカルリソースを復元する親クラスのメソッドを呼び出します。次に、すべてのアニメーションフレームオブジェクトのピクセル配列をリセットして、フォームの外観を復元した後、必要な座標と保存された背景領域のサイズで背景をコピーできるようにします。
これで、フォームに正多角形を描画するテストを行う準備ができました。
検証
テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part80\にTestDoEasyPart80.mq5として保存します。
前回の記事では、キーを押してフォームオブジェクトに図形を描画しました。ここでも同じことを使用と思います。座標とサイズが動的に設定されている多角形を描画するキーを再割り当てするだけです。ここでは、X軸に沿ったアニメーションフレームの座標と、描画された多角形の頂点の数(3から10)も動的に変更します。
- Y – 正多角形(平滑化なし)
- U – 正多角形(平滑化なし、塗りつぶし)
- I – アンチエイリアシング(AA)を使用した正多角形
- O – 正多角形(Wu)
- P – 2つの平滑化アルゴリズム(Smooth)を適用した、指定された幅の正多角形
- A – 予備ソートによる平滑化(Thick)を適用した、指定された幅の正多角形
- . – 塗りつぶされた領域を描画(実際、これはフォーム全体を指定された色で塗りつぶすことを意味します)
次に、フォームをクリックするたびに、描画されたフレームのX座標が変更され、描画された多角形の頂点の数が1つ増えます。
「TEXT_ANCHOR」部分文字列のすべての出現箇所を「FRAME_ANCHOR」に置き換えます。
EAの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 ArrayResize(array_clr,2); array_clr[0]=C'26,100,128'; // Original ≈Dark-azure color array_clr[1]=C'35,133,169'; // Lightened original color //--- Create the specified number of form objects list_forms.Clear(); int total=FORMS_TOTAL; for(int i=0;i<total;i++) { int y=40; if(i>0) { CForm *form_prev=list_forms.At(i-1); if(form_prev==NULL) continue; y=form_prev.BottomEdge()+10; } //--- When creating an object, pass all the required parameters to it CForm *form=new CForm("Form_0"+(string)(i+1),300,y,100,(i<2 ? 70 : 30)); if(form==NULL) continue; //--- Set activity and moveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the form ID equal to the loop index and the index in the list of objects form.SetID(i); form.SetNumber(0); // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them //--- Set the partial opacity for the middle form and the full one for the rest uchar opacity=(i==1 ? 250 : 255); //--- Set the form style and its color theme depending on the loop index if(i<2) { ENUM_FORM_STYLE style=(ENUM_FORM_STYLE)i; ENUM_COLOR_THEMES theme=(ENUM_COLOR_THEMES)i; //--- Set the form style and theme form.SetFormStyle(style,theme,opacity,true,false); } //--- If this is the first (top) form if(i==0) { //--- Draw a concave field slightly shifted from the center of the form downwards form.DrawFieldStamp(3,10,form.Width()-6,form.Height()-13,form.ColorBackground(),form.Opacity()); form.Done(); } //--- If this is the second form if(i==1) { //--- Draw a concave semi-transparent "tainted glass" field in the center form.DrawFieldStamp(10,10,form.Width()-20,form.Height()-20,clrWheat,200); form.Done(); } //--- If this is the third form if(i==2) { //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(clrDarkBlue); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(3,3,clr,200,4); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity()); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); form.Done(); //--- Display the text describing the gradient type and update the form //--- Text parameters: the text coordinates and the anchor point in the form center //--- Create a new text animation frame with the ID of 0 and display the text on the form form.TextOnBG(0,TextByLanguage("V-Градиент","V-Gradient"),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,false); } //--- If this is the fourth (bottom) form if(i==3) { //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(clrDarkBlue); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(3,3,clr,200,4); //--- Fill the form background with a horizontal gradient form.Erase(array_clr,form.Opacity(),false); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); form.Done(); //--- Display the text describing the gradient type and update the form //--- Text parameters: the text coordinates and the anchor point in the form center //--- Create a new text animation frame with the ID of 0 and display the text on the form form.TextOnBG(0,TextByLanguage("H-Градиент","H-Gradient"),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); } //--- Add objects to the list if(!list_forms.Add(form)) { delete form; continue; } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
OnChartEvent()ハンドラのキーストロークを処理するブロックで、フォームの外観を復元し、フレームオブジェクトピクセルの配列をゼロにリセットするメソッドの呼び出しを実装します。
//--- If a key is pressed if(id==CHARTEVENT_KEYDOWN) { //--- Get a drawn shape type depending on a pressed key figure_type=FigureType(lparam); //--- If the shape type has changed if(figure_type!=figure_type_prev) { //--- Get the text of the drawn shape type description figure=FigureTypeDescription(figure_type); //--- In the loop by all forms, for(int i=0;i<list_forms.Total();i++) { //--- get the pointer to the next form object CForm *form=list_forms.At(i); if(form==NULL) continue; //--- If the form ID is 2, if(form.ID()==2) { //--- Reset all coordinate shifts to zero, restore the form background and display the text describing the drawn shape type nx1=ny1=nx2=ny2=nx3=ny3=nx4=ny4=nx5=ny5=0; form.Reset(); form.TextOnBG(0,figure,form.TextLastX(),form.TextLastY(),form.TextAnchor(),C'211,233,149',255,false,true); } } //--- Write the new shape type figure_type_prev=figure_type; } }
FigureType()関数に「.」キーの処理を追加します。
//+------------------------------------------------------------------+ //| Return the shape depending on the pressed key | //+------------------------------------------------------------------+ ENUM_FIGURE_TYPE FigureType(const long key_code) { switch((int)key_code) { //--- "1" = Dot case 49 : return FIGURE_TYPE_PIXEL; //--- "2" = Dot with AntiAlliasing case 50 : return FIGURE_TYPE_PIXEL_AA; //--- "3" = Vertical line case 51 : return FIGURE_TYPE_LINE_VERTICAL; //--- "4" = Vertical segment of a freehand line having a specified width using a smoothing algorithm case 52 : return FIGURE_TYPE_LINE_VERTICAL_THICK; //--- "5" = Horizontal line case 53 : return FIGURE_TYPE_LINE_HORIZONTAL; //--- "6" = Horizontal segment of a freehand line having a specified width using a smoothing algorithm case 54 : return FIGURE_TYPE_LINE_HORIZONTAL_THICK; //--- "7" = Freehand line case 55 : return FIGURE_TYPE_LINE; //--- "8" = Line with AntiAlliasing case 56 : return FIGURE_TYPE_LINE_AA; //--- "9" = Line with WU case 57 : return FIGURE_TYPE_LINE_WU; //--- "0" = Segment of a freehand line having a specified width using a smoothing algorithm case 48 : return FIGURE_TYPE_LINE_THICK; //--- "q" = Polyline case 81 : return FIGURE_TYPE_POLYLINE; //--- "w" = Polyline with AntiAlliasing case 87 : return FIGURE_TYPE_POLYLINE_AA; //--- "e" = Polyline with WU case 69 : return FIGURE_TYPE_POLYLINE_WU; //--- "r" = Polyline with a specified width using two smoothing algorithms case 82 : return FIGURE_TYPE_POLYLINE_SMOOTH; //--- "t" = Polyline with a specified width using a smoothing algorithm case 84 : return FIGURE_TYPE_POLYLINE_THICK; //--- "y" = Polygon case 89 : return FIGURE_TYPE_POLYGON; //--- "u" = Filled polygon case 85 : return FIGURE_TYPE_POLYGON_FILL; //--- "i" = Polygon with AntiAlliasing case 73 : return FIGURE_TYPE_POLYGON_AA; //--- "o" = Polygon with WU case 79 : return FIGURE_TYPE_POLYGON_WU; //--- "p" = Polygon with a specified width using two smoothing algorithms case 80 : return FIGURE_TYPE_POLYGON_SMOOTH; //--- "a" = Polygon with a specified width using a smoothing algorithm case 65 : return FIGURE_TYPE_POLYGON_THICK; //--- "s" = Rectangle case 83 : return FIGURE_TYPE_RECTANGLE; //--- "d" = Filled rectangle case 68 : return FIGURE_TYPE_RECTANGLE_FILL; //--- "f" = Circle case 70 : return FIGURE_TYPE_CIRCLE; //--- "g" = Filled circle case 71 : return FIGURE_TYPE_CIRCLE_FILL; //--- "h" = Circle with AntiAlliasing case 72 : return FIGURE_TYPE_CIRCLE_AA; //--- "j" = Circle with WU case 74 : return FIGURE_TYPE_CIRCLE_WU; //--- "k" = Triangle case 75 : return FIGURE_TYPE_TRIANGLE; //--- "l" = Filled triangle case 76 : return FIGURE_TYPE_TRIANGLE_FILL; //--- "z" = Triangle with AntiAlliasing case 90 : return FIGURE_TYPE_TRIANGLE_AA; //--- "x" = Triangle with WU case 88 : return FIGURE_TYPE_TRIANGLE_WU; //--- "c" = Ellipse case 67 : return FIGURE_TYPE_ELLIPSE; //--- "v" = Filled ellipse case 86 : return FIGURE_TYPE_ELLIPSE_FILL; //--- "b" = Ellipse with AntiAlliasing case 66 : return FIGURE_TYPE_ELLIPSE_AA; //--- "n" = Ellipse with WU case 78 : return FIGURE_TYPE_ELLIPSE_WU; //--- "m" = Ellipse arc case 77 : return FIGURE_TYPE_ARC; //--- "," = Ellipse sector case 188 : return FIGURE_TYPE_PIE; //--- "." = Filled area case 190 : return FIGURE_TYPE_FILL; //--- Default = Dot default : return FIGURE_TYPE_PIXEL; } } //+------------------------------------------------------------------+
FigureProcessing()関数で、座標配列を動的にします。
//+------------------------------------------------------------------+ //| Handle the selected shape | //+------------------------------------------------------------------+ void FigureProcessing(CForm *form,const ENUM_FIGURE_TYPE figure_type) { int array_x[]; int array_y[]; switch(figure_type) {
また、座標配列をクラスメソッドに渡す必要がある場合は常に配列サイズを設定します。
//--- "q" = Polyline case FIGURE_TYPE_POLYLINE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*8; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; coordX5=coordX1; coordY5=coordY1; //--- Fill in the arrays with coordinate values ArrayResize(array_x,5); ArrayResize(array_y,5); array_x[0]=coordX1; array_x[1]=coordX2; array_x[2]=coordX3; array_x[3]=coordX4; array_x[4]=coordX5; array_y[0]=coordY1; array_y[1]=coordY2; array_y[2]=coordY3; array_y[3]=coordY4; array_y[4]=coordY5; //--- check x1 and y1 coordinates for being outside the form
...
//--- "w" = Polyline with AntiAlliasing case FIGURE_TYPE_POLYLINE_AA : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*8; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; coordX5=coordX1; coordY5=coordY1; //--- Fill in the arrays with coordinate values ArrayResize(array_x,5); ArrayResize(array_y,5); array_x[0]=coordX1; array_x[1]=coordX2; array_x[2]=coordX3; array_x[3]=coordX4; array_x[4]=coordX5; array_y[0]=coordY1; array_y[1]=coordY2; array_y[2]=coordY3; array_y[3]=coordY4; array_y[4]=coordY5; //--- check x1 and y1 coordinates for being outside the form
...
//--- "e" = Polyline with WU case FIGURE_TYPE_POLYLINE_WU : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*8; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; coordX5=coordX1; coordY5=coordY1; //--- Fill in the arrays with coordinate values ArrayResize(array_x,5); ArrayResize(array_y,5); array_x[0]=coordX1; array_x[1]=coordX2; array_x[2]=coordX3; array_x[3]=coordX4; array_x[4]=coordX5; array_y[0]=coordY1; array_y[1]=coordY2; array_y[2]=coordY3; array_y[3]=coordY4; array_y[4]=coordY5; //--- check x1 and y1 coordinates for being outside the form
...
//--- "r" = Polyline with a specified width using two smoothing algorithms case FIGURE_TYPE_POLYLINE_SMOOTH : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*8; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; coordX5=coordX1; coordY5=coordY1; //--- Fill in the arrays with coordinate values ArrayResize(array_x,5); ArrayResize(array_y,5); array_x[0]=coordX1; array_x[1]=coordX2; array_x[2]=coordX3; array_x[3]=coordX4; array_x[4]=coordX5; array_y[0]=coordY1; array_y[1]=coordY2; array_y[2]=coordY3; array_y[3]=coordY4; array_y[4]=coordY5; //--- check x1 and y1 coordinates for being outside the form
...
//--- "t" = Polyline with a specified width using a smoothing algorithm case FIGURE_TYPE_POLYLINE_THICK : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*8; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; coordX5=coordX1; coordY5=coordY1; //--- Fill in the arrays with coordinate values ArrayResize(array_x,5); ArrayResize(array_y,5); array_x[0]=coordX1; array_x[1]=coordX2; array_x[2]=coordX3; array_x[3]=coordX4; array_x[4]=coordX5; array_y[0]=coordY1; array_y[1]=coordY2; array_y[2]=coordY3; array_y[3]=coordY4; array_y[4]=coordY5; //--- check x1 and y1 coordinates for being outside the form
多角形を描画するためのキーストロークを処理するコードは、正多角形を描画するためのメソッドの呼び出しで置き換えられます。
//--- "y" = Polygon case FIGURE_TYPE_POLYGON : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,clrAliceBlue); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "u" = Filled polygon case FIGURE_TYPE_POLYGON_FILL : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonFillOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,clrLightCoral); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "i" = Polygon with AntiAlliasing case FIGURE_TYPE_POLYGON_AA : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonAAOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,clrLightCyan); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "o" = Polygon with WU case FIGURE_TYPE_POLYGON_WU : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonWuOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,clrLightGoldenrod); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "p" = Polygon with a specified width using two smoothing algorithms case FIGURE_TYPE_POLYGON_SMOOTH : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonSmoothOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,3,clrLightGreen,255,0.5,10.0,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "a" = Polygon with a specified width using a smoothing algorithm case FIGURE_TYPE_POLYGON_THICK : coordX1=START_X+nx1; // X coordinate coordY1=START_Y; // Y coordinate coordX2=3+nx2*4; // Length of square sides coordY2=3+ny2; // Number of faces coordX3=0; // Rotation angle //--- check the square side length for exceeding the double form height if(coordX2>form.Height()*2) { nx2=0; coordX2=3; } //--- check the x1 coordinate for exceeding the form boundaries if(coordX1>form.Width()-1) { nx1=0; coordX1=-coordX2; } //--- check the number of faces for exceeding 10 if(coordY2>16) { ny2=0; coordY2=3; } //--- check the rotation angle for exceeding 360 degrees if(coordX3>360) { nx3=0; coordX3=0; } //--- Draw a shape form.DrawNgonThickOnBG(0,coordY2,coordX1,coordY1,coordX2,(double)coordX3,5,clrLightSalmon,255,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; break; //--- "s" = Rectangle
コードには詳細なコメントがあるので、すべてが明確なはずです。質問がある場合は、コメント欄でお気軽にお問い合わせください。
フォームを色で塗りつぶすために「.」キーの押下処理を追加するのをを忘れないでください。
//--- "." = Filled area case FIGURE_TYPE_FILL : coordX1=START_X+nx1; coordY1=START_Y+ny1; form.FillOnBG(0,coordX1,coordY1,clrLightSteelBlue,255,10); break; //--- Default = Nothing default : break; } } //+------------------------------------------------------------------+
EAをコンパイルし、銘柄チャートで起動します。
起動後、キーを押して正多角形を描画し、領域を色で塗りつぶします。
すべてが意図したとおりに機能します。しかし、形状がかなり不均一であることが判明しました... 私の意見では、Wu平滑アルゴリズムを適用した多角形の外観が最適です。塗りつぶし中に、必要なしきい値パラメータを指定することで、塗りつぶしの程度(しきい値)を調整できます。
form.FillOnBG(0,coordX1,coordY1,clrLightSteelBlue,255,10);
次の段階
次の記事では、アニメーションとフォームオブジェクトの開発を続けます。
ライブラリの現在のバージョンのすべてのファイルは、テストおよびダウンロードできるように、MQL5のテストEAファイルと一緒に以下に添付されています。
質問や提案はコメント欄にお願いします。
**連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第73部): グラフィック要素のフォームオブジェクト
DoEasyライブラリのグラフィックス(第74部): CCanvasクラスを使用した基本的グラフィック要素
DoEasyライブラリのグラフィックス(第75部): 基本的なグラフィック要素でプリミティブとテキストを処理するメソッド
DoEasyライブラリのグラフィックス(第76部): フォームオブジェクトと事前定義されたカラースキーム
DoEasyライブラリのグラフィックス(第77部): 影オブジェクトクラス
DoEasyライブラリのグラフィックス(第78部): ライブラリのアニメーションの原則イメージスライス
DoEasyライブラリのグラフィックス(第79部): 「アニメーションフレーム」オブジェクトクラスとその子孫オブジェクト
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/9689





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索