DoEasy 函数库中的图形(第七十九部分):“动画框”对象类及其衍生对象
Artyom Trishkin | 27 九月, 2021
内容
概述
在上一篇文章中,我已创建了类,来保存和之后恢复位于所绘制造型的下层背景部分。 在此,我将重启这个概念的探究,并基于它创建若干个类:单个动画框、及其衍生类 — 文本动画框和矩形动画框类。
基类包含单个动画框的一套通用属性,而其衍生类将拥有自己固有的绘制造型的方法。 文本动画类能够操控文本,而矩形动画框能够创建单个动画框,并利用基于诸如 CCanvas 类的绘制方法在其内绘制各种造型。
每个所创建交互窗对象都有一套在画布上绘图的自定义方法,这令我们可在交互窗上快速创建和管理新的图像。 为了能够在每个交互窗中方便地运用绘图工具,我将创建一个通用类来显示交互窗上所有已创建文本和形状图像的列表。 稍后,我还会添加新的动画方法,它们的清单也将放置到通用类之中。 这样的概念允许我们动态创建新图像,并将它们保存到相应的列表当中。 接下来,可以从交互窗对象中快速恢复它们,并显示在其背景之上。 在这种情况下,该类对象会自动将交互窗的背景保存在它们之下。 如果对象被删除、更改或移动,则恢复所保存的背景。
因此,在本篇文章中,我将略微修改前几篇文章里创建的绘制框的代码,开发基本动画框对象类,并开发两个衍生类 — 文本动画框和矩形动画框类。 我们来创建存储这些框对象列表的类,并提供从交互窗对象中操控它们的能力。
改进库类
首先,我们改进之前创建的库类。 在 \MQL5\Include\DoEasy\Defines.mqh 里,在矩形动画框类中添加动画框列表和所绘制造型类型列表:
//+------------------------------------------------------------------+ //| 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, // Rectangle animation frame }; //+------------------------------------------------------------------+ //| 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, // 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 }; //+------------------------------------------------------------------+
我将用动画框类型来识别动画框对象(无论是文本、绘制的造型、还是我将在后续文章中引入的任何其它动画框类型)。 绘制造型的类型可准确指示单个矩形动画框中的绘制内容。 这些类型对应于 CCanvas 类中现有的绘制方法(在类方法表格中的“数据访问”、“绘制图元”、“绘制填充图元" 和 "运用抗锯齿绘制图元 ”部分)。
在 \MQL5\Include\DoEasy\Data.mqh 里,加入新的消息索引:
//--- CForm MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ, // Failed to create new shadow object MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ, // Failed to create new pixel copier object MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST, // Pixel copier object with ID already present in the list MSG_FORM_OBJECT_PC_OBJ_NOT_EXIST_LIST, // No pixel copier object with ID in the list //--- CFrame MSG_FORM_OBJECT_ERR_FAILED_CREATE_FRAME, // Failed to create a new animation frame object MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST, // Animation frame object with ID already present in the list MSG_FORM_OBJECT_FRAME_NOT_EXIST_LIST, // Animation frame object with ID not present in the list //--- CShadowObj MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE, // Error! Image size too small or blur too extensive
及与新添加的索引相对应的消息文本:
//--- CForm {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj () method"}, {"Не удалось создать новый объект для тени","Failed to create new object for shadow"}, {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"}, {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "}, {"В списке нет объекта-копировщика пикселей с идентификатором ","No pixel copier object with ID "}, //--- CFrame {"Не удалось создать новый объект-кадр анимации","Failed to create new animation frame object"}, {"В списке уже есть объект-кадр анимации с идентификатором ","The list already contains an animation frame object with an ID "}, {"В списке нет объекта-кадра анимации с идентификатором ","No animation frame object with ID "}, //--- CShadowObj {"Ошибка! Размер изображения очень маленький или очень большое размытие","Error! Image size is very small or very large blur"},
在函数库服务函数文件 \MQL5\Include\DoEasy\Services\DELib.mqh中,添加返回数组中最大值和最小值的函数:
//+------------------------------------------------------------------+ //| Return the maximum value in the array | //+------------------------------------------------------------------+ template<typename T> bool ArrayMaximumValue(const string source,const T &array[],T &max_value) { if(ArraySize(array)==0) { CMessage::ToLog(source,MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY); return false; } max_value=0; int index=ArrayMaximum(array); if(index==WRONG_VALUE) return false; max_value=array[index]; return true; } //+------------------------------------------------------------------+ //| Return the minimum value in the array | //+------------------------------------------------------------------+ template<typename T> bool ArrayMinimumValue(const string source,const T &array[],T &min_value) { if(ArraySize(array)==0) { CMessage::ToLog(source,MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY); return false; } min_value=0; int index=ArrayMinimum(array); if(index==WRONG_VALUE) return false; min_value=array[index]; return true; } //+------------------------------------------------------------------+
这些函数依据所传递的引用,返回位于数组中的最大值或最小值。 因为位于数组单元格中的数值可能是“无效”的,故函数的任何返回值都是 bool 类型。 例如,如果从数组收到的数据返回 -1(就像在许多函数中所做的那样),则这样的值可以是数组中设置的数值之一。 当返回有效值 (-1) 时,我们的程序假定这是一个错误。 这是不正确的。 因此,如果出现错误,我们将返回 false,而在数组中找到的最大值或最小值则赋值给通过引用传递给函数的变量。 如果函数返回 true,则变量里存储所需的数值。 source 变量接收所调用函数的方法名称。 如若发生错误,这可以令我们查看调用该函数的方法名称,以及错误消息。
该变量还接收函数返回的绘制造型类型的描述:
//+------------------------------------------------------------------+ //| Return the description of the drawn shape type | //+------------------------------------------------------------------+ string FigureTypeDescription(const ENUM_FIGURE_TYPE figure_type) { return(StringSubstr(EnumToString(figure_type),12)); } //+------------------------------------------------------------------+
在此,以枚举格式传递给函数的类型被转换为字符串描述。 从第 12 个字符位置开始的子字符串是从类型文本表达中提取出来的,以便裁剪不必要的文本。 例如,FIGURE_TYPE_TRIANGLE 图形类型转换为 "FIGURE_TYPE_TRIANGLE",且必要子串 就是从这个文本 "FIGURE_TYPE_TRIANGLE" 中第 12 个字符开始提取出来的。 字符串 “TRIANGLE” 就是返回的结果。
在前一篇文章中,当创建把一部分背景复制到数组的方法时,我是通过交互窗上显示的文本大小来定义索要复制的背景矩形的大小和坐标。 在此,我会显示图像。 它们的大小不再由文本大小定义。 因此,我们需要创建一个方法来定义背景部分需复制矩形的坐标和大小。
在图形元素类的 \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh 文件中,重命名方法
//--- Return coordinate offsets relative to the text anchor point void TextGetShiftXY(const string text, // Text for calculating the size of its outlining rectangle const ENUM_TEXT_ANCHOR anchor,// Text anchor point, relative to which the offsets are calculated int &shift_x, // X coordinate of the rectangle upper left corner int &shift_y); // Y coordinate of the rectangle upper left corner
现在该方法称为 GetShiftXYbyText()。 我们来声明一个新方法,按照相对于对象锚点所的指定大小,返回需复制图像部分的坐标和大小:
//--- Return coordinate offsets relative to the text anchor point by text void GetShiftXYbyText(const string text, // Text for calculating the size of its outlining rectangle const ENUM_TEXT_ANCHOR anchor, // Text anchor point, relative to which the offsets are calculated int &shift_x, // X coordinate of the rectangle upper left corner int &shift_y); // Y coordinate of the rectangle upper left corner //--- Return coordinate offsets relative to the rectangle anchor point by size void GetShiftXYbySize(const int width, //Rectangle size by width const int height, //Rectangle size by height const ENUM_TEXT_ANCHOR anchor, // Rectangle anchor point, relative to which the offsets are calculated int &shift_x, // X coordinate of the rectangle upper left corner int &shift_y); // Y coordinate of the rectangle upper left corner
在类清单的末尾实现它们。
该方法按大小返回相对于矩形锚点的坐标偏移量:
//+------------------------------------------------------------------+ //| Return coordinate offsets relative to the rectangle anchor point | //| by size | //+------------------------------------------------------------------+ void CGCnvElement::GetShiftXYbySize(const int width,const int height,const ENUM_TEXT_ANCHOR anchor,int &shift_x,int &shift_y) { switch(anchor) { case TEXT_ANCHOR_LEFT_TOP : shift_x=0; shift_y=0; break; case TEXT_ANCHOR_LEFT_CENTER : shift_x=0; shift_y=-height/2; break; case TEXT_ANCHOR_LEFT_BOTTOM : shift_x=0; shift_y=-height; break; case TEXT_ANCHOR_CENTER_TOP : shift_x=-width/2; shift_y=0; break; case TEXT_ANCHOR_CENTER : shift_x=-width/2; shift_y=-height/2; break; case TEXT_ANCHOR_CENTER_BOTTOM : shift_x=-width/2; shift_y=-height; break; case TEXT_ANCHOR_RIGHT_TOP : shift_x=-width; shift_y=0; break; case TEXT_ANCHOR_RIGHT_CENTER : shift_x=-width; shift_y=-height/2; break; case TEXT_ANCHOR_RIGHT_BOTTOM : shift_x=-width; shift_y=-height; break; default : shift_x=0; shift_y=0; break; } } //+------------------------------------------------------------------+
在此,根据复制区域的宽度和高度,以及传递给方法的锚点,计算相对于锚点的坐标偏移,并将它们赋值给通过引用传递给方法的变量。
该方法返回相对于文本锚点的坐标偏移:
//+------------------------------------------------------------------+ //| Return coordinate offsets relative to the text anchor point | //+------------------------------------------------------------------+ void CGCnvElement::GetShiftXYbyText(const string text,const ENUM_TEXT_ANCHOR anchor,int &shift_x,int &shift_y) { int tw=0,th=0; this.TextSize(text,tw,th); this.GetShiftXYbySize(tw,th,anchor,shift_x,shift_y); } //+------------------------------------------------------------------+
此处我们首先定义传递给方法的文本大小,并调用上面研究过的方法定义保存交互窗背景图片区域的坐标偏移。
在前一篇文章中,我曾开发了复制交互窗背景图像一部分,并在之后从数组中恢复它的类。 该类是在交互窗对象类文件中设置的。 现在我从交互窗对象类文件中删除该类,将其重新定位到新创建的框对象类的文件中(我再次发现在单独的文件中编写和存储类更好)。
因此,我们为单个动画框创建一个基类。 该类将包含其所有衍生后代共有的属性。
“动画框”对象类
在 \MQL5\Include\DoEasy\Objects\Graph\ 中,创建新 Animations\ 文件夹,内含 CFrame 类的新 Frame.mqh 文件。
图形元素对象类的文件应该包含到类文件中:
//+------------------------------------------------------------------+ //| Frame.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 "..\GCnvElement.mqh" //+------------------------------------------------------------------+
接下来,放置从交互窗对象类文件中移除的像素复印机对象类(在上一篇文章中已研究过):
//+------------------------------------------------------------------+ //| Frame.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 "..\GCnvElement.mqh" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CPixelCopier : public CObject { protected: CGCnvElement *m_element; // Pointer to the graphical element uint m_array[]; // Pixel array int m_id; // ID int m_x; // X coordinate of the upper left corner int m_y; // Y coordinate of the upper left corner int m_w; // Copied image width int m_h; // Copied image height int m_wr; // Calculated copied image width int m_hr; // Calculated copied image height public: //--- Compare CPixelCopier objects by a specified property (to sort the list by an object property) virtual int Compare(const CObject *node,const int mode=0) const { const CPixelCopier *obj_compared=node; return(mode==0 ? (this.ID()>obj_compared.ID() ? 1 : this.ID()<obj_compared.ID() ? -1 : 0) : WRONG_VALUE); } //--- Set the properties void SetElement(CGCnvElement *element) { this.m_element=element; } void SetID(const int id) { this.m_id=id; } void SetCoordX(const int value) { this.m_x=value; } void SetCoordY(const int value) { this.m_y=value; } void SetWidth(const int value) { this.m_w=value; } void SetHeight(const int value) { this.m_h=value; } //--- Get the properties int ID(void) const { return this.m_id; } int CoordX(void) const { return this.m_x; } int CoordY(void) const { return this.m_y; } int Width(void) const { return this.m_w; } int Height(void) const { return this.m_h; } int WidthReal(void) const { return this.m_wr; } int HeightReal(void) const { return this.m_hr; } //--- Copy the part or the entire image to the array bool CopyImgDataToArray(const uint x_coord,const uint y_coord,uint width,uint height); //--- Copy the part or the entire image from the array to the canvas bool CopyImgDataToCanvas(const int x_coord,const int y_coord); //--- Constructors CPixelCopier (void){;} CPixelCopier (const int id, const int x, const int y, const int w, const int h, CGCnvElement *element) : m_id(id), m_x(x),m_y(y),m_w(w),m_wr(w),m_h(h),m_hr(h) { this.m_element=element; } ~CPixelCopier (void){;} }; //+------------------------------------------------------------------+ //| Copy part or all of the image to the array | //+------------------------------------------------------------------+ bool CPixelCopier::CopyImgDataToArray(const uint x_coord,const uint y_coord,uint width,uint height) { //--- Assign coordinate values, passed to the method, to the variables int x1=(int)x_coord; int y1=(int)y_coord; //--- If X coordinates goes beyond the form on the right or Y coordinate goes beyond the form at the bottom, //--- there is nothing to copy, the copied area is outside the form. Return 'false' if(x1>this.m_element.Width()-1 || y1>this.m_element.Height()-1) return false; //--- Assign the width and height values of the copied area to the variables //--- If the passed width and height are equal to zero, assign the form width and height to them this.m_wr=int(width==0 ? this.m_element.Width() : width); this.m_hr=int(height==0 ? this.m_element.Height() : height); //--- If X and Y coordinates are equal to zero (the upper left corner of the form), as well as the width and height are equal to the form width and height, //--- the copied area is equal to the entire form area. Copy the entire form (returning it from the method) using the ImageCopy() method //if(x1==0 && y1==0 && this.m_wr==this.m_element.Width() && this.m_hr==this.m_element.Height()) // return this.m_element.ImageCopy(DFUN,this.m_array); //--- Calculate the right X coordinate and lower Y coordinate of the rectangle area int x2=int(x1+this.m_wr-1); int y2=int(y1+this.m_hr-1); //--- If the calculated X coordinate goes beyond the form, the right edge of the form will be used as the coordinate if(x2>=this.m_element.Width()-1) x2=this.m_element.Width()-1; //--- If the calculated Y coordinate goes beyond the form, the bottom edge of the form will be used as the coordinate if(y2>=this.m_element.Height()-1) y2=this.m_element.Height()-1; //--- Calculate the copied width and height this.m_wr=x2-x1+1; this.m_hr=y2-y1+1; //--- Define the necessary size of the array, which is to store all image pixels with calculated width and height int size=this.m_wr*this.m_hr; //--- If failed to set the array size, inform of that and return 'false' if(::ArrayResize(this.m_array,size)!=size) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE,true); return false; } //--- Set the index in the array for recording the image pixel int n=0; //--- In a loop by the calculated height of the copied area, starting from the specified Y coordinate for(int y=y1;y<y1+this.m_hr;y++) { //--- in a loop by the calculated width of the copied area, starting from the specified X coordinate for(int x=x1;x<x1+this.m_wr;x++) { //--- Copy the next image pixel to the array and increase the array index this.m_array[n]=this.m_element.GetCanvasObj().PixelGet(x,y); n++; } } //--- Successful - return 'true' return true; } //+------------------------------------------------------------------+ //| Copy the part or the entire image from the array to the canvas | //+------------------------------------------------------------------+ bool CPixelCopier::CopyImgDataToCanvas(const int x_coord,const int y_coord) { //--- If the array of saved pixels is empty, inform of that and return 'false' int size=::ArraySize(this.m_array); if(size==0) { CMessage::ToLog(DFUN,MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY,true); return false; } //--- Set the index of the array for reading the image pixel int n=0; //--- In a loop by the previously calculated height of the copied area, starting from the specified Y coordinate for(int y=y_coord;y<y_coord+this.m_hr;y++) { //--- in a loop by the previously calculated width of the copied area, starting from the specified X coordinate for(int x=x_coord;x<x_coord+this.m_wr;x++) { //--- Restore the next image pixel from the array and increase the array index this.m_element.GetCanvasObj().PixelSet(x,y,this.m_array[n]); n++; } } return true; } //+------------------------------------------------------------------+
此处唯一(暂时)更改的是我把应该在复制之前保存的整个交互窗图像的代码注释掉了。 此处发生的错误导致直接从图形资源复制背景,而非从交互窗对象数组。 图形资源包含应用于交互窗背景图像的所有变化。 为了修复这个问题,我需要将交互窗外观保存到一个单独的数组当中,其中包含原始交互窗图像的副本。 我已经有了一个这样的数组,但我仍然需要创建方法,在原始交互窗外观创建完毕后立即将其保存。 至此,我已经注释掉了这些代码。 含有整个交互窗(而不是其部分)大小的背景,将在循环中恢复交互窗背景部分(即,并非将一个数组复制到另一个数组,而是来自存储在交互窗背景图像部分副本的数组)。
接下来,在像素复制器类清单末尾,编写动画框对象类的主体:
//+------------------------------------------------------------------+ //| 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_TEXT_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 public: //--- 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_TEXT_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: //--- Text frame constructor CFrame(const int id, const int x, const int y, const string text, CGCnvElement *element); //--- Rectangle frame constructor CFrame(const int id, const int x, const int y, const int w, const int h, CGCnvElement *element); }; //+------------------------------------------------------------------+
该类派生自像素复印机对象类,所以,实际上就是像素复印机对象类。
类中声明的所有变量和方法都已在注释中讲述。 鉴于该类是其它动画框类的基类,因此所有衍生后代的通用属性和方法都在这里设置。
返回最后坐标、偏移量和锚点的变量和方法是必要的,以便我们能够在恢复图像时确定之前保存的图像部分的坐标。 这些坐标保存的是绘制图像时被擦除的背景。
该类拥有三个构造函数:
- 默认的公开构造函数,
- 受保护的文本框对象构造函数,
- 受保护的矩形框对象构造函数,
我们来研究受保护构造函数的实现。
矩形框的构造函数:
//+------------------------------------------------------------------+ //| Constructor of rectangle frames | //+------------------------------------------------------------------+ CFrame::CFrame(const int id,const int x,const int y,const int w,const int h,CGCnvElement *element) : CPixelCopier(id,x,y,w,h,element) { this.m_frame_figure_type=ANIMATION_FRAME_TYPE_QUAD; this.m_anchor_last=TEXT_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; } //+------------------------------------------------------------------+
构造函数接收欲创建矩形框对象的 ID、其 X 和 Y 坐标、框的宽度和高度,以及指向欲创建新对象的图形元素对象的指针。 由于该类是像素复制器对象的衍生后代,因此在构造函数初始化清单中要将所有必要的参数传递给基类的构造函数。 这些参数都是需要传递给构造函数参数的属性。
为类主体中的所有类成员变量设置默认参数。
文本框的构造函数:
//+------------------------------------------------------------------+ //| The constructor of text frames | //+------------------------------------------------------------------+ CFrame::CFrame(const int id, const int x, const int y, const string text, CGCnvElement *element) { int w=0,h=0; this.m_element=element; this.m_element.GetCanvasObj().TextSize(text,w,h); this.m_anchor_last=this.m_element.TextAnchor(); this.m_frame_figure_type=ANIMATION_FRAME_TYPE_TEXT; this.m_x_last=x; this.m_y_last=y; this.m_shift_x_prev=0; this.m_shift_y_prev=0; CPixelCopier::SetID(id); CPixelCopier::SetCoordX(x); CPixelCopier::SetCoordY(y); CPixelCopier::SetWidth(w); CPixelCopier::SetHeight(h); } //+------------------------------------------------------------------+
构造函数接收欲创建文本框对象的 ID、其 X 和 Y 坐标、文本和指向欲创建新对象的图形元素对象的指针。
在类主体中,首先定义文本大小,然后设置类成员变量的默认值,并将欲创建对象的 ID、其坐标和文本大小赋值给父类像素复制机对象。
创建动画帧对象类衍生后代的对象类。
文本动画框类
在 \MQL5\Include\DoEasy\Objects\Graph\Animations\ 里,创建 CFrameText 类的新文件 FrameText.mqh。
动画框类的文件应该包含在该文件之中,而类本身应该是其衍生后代:
//+------------------------------------------------------------------+ //| FrameText.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" //+------------------------------------------------------------------+ //| 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_TEXT_ANCHOR anchor,const color clr,const uchar opacity,bool redraw=false); //--- Constructors CFrameText() {;} CFrameText(const int id,CGCnvElement *element) : CFrame(id,0,0,"",element) {} }; //+------------------------------------------------------------------+
在此我们可以看到一个在交互窗对象背景上绘制文本的公开方法,和两个构造函数 — 默认和参数型。
参数型构造函数接收欲创建文本动画框对象的 ID,和指向欲创建对象的图形元素的指针。 在初始化清单中,父类接收在构造函数参数中传递的 ID、坐标和文本的默认值,以及在构造函数参数中传递的指向图形元素的指针。
在背景上显示文本,同时保存和恢复背景的方法:
//+-------------------------------------------------------------------------------+ //| 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_TEXT_ANCHOR anchor,const color clr,const uchar opacity,bool redraw=false) { //--- Find out the width and height of the text outlining the rectangle (to be used as the size of the saved area) int w=0,h=0; this.m_element.TextSize(text,w,h); //--- Calculate coordinate offsets for the saved area depending on the text anchor point int shift_x=0,shift_y=0; this.m_element.GetShiftXYbySize(w,h,anchor,shift_x,shift_y); //--- If the pixel array is not empty, the background under the text 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 text is successfully saved if(!CPixelCopier::CopyImgDataToArray(x+shift_x,y+shift_y,w,h)) return false; //--- Draw the text and update the element this.m_element.Text(x,y,text,clr,opacity,anchor); this.m_element.Update(redraw); this.m_anchor_last=anchor; this.m_x_last=x; this.m_y_last=y; this.m_shift_x_prev=shift_x; this.m_shift_y_prev=shift_y; return true; } //+------------------------------------------------------------------+
该方法的逻辑在代码注释中已有详述。 上一篇文章中我已在测试过程中研究过它了(与在测试 EA 部分的 OnChartEvent() 处理程序中设置的逻辑类似),所以,我相信,此处的一切也都很清晰。 在交互窗上绘制文本之后,在父类的变量中设置其锚点、X 和 Y 坐标,以及相对于锚点的偏移值。 它们的值将用于恢复被文本覆盖的交互窗图像背景。
现在,我们来创建动画帧对象的第二个衍生后代类。
矩形动画框类
在 \MQL5\Include\DoEasy\Objects\Graph\Animations\ 里,创建 CFrameQuad 类的新文件 FrameQuad.mqh。
父类文件应该包含在(以及派生自)类文件:
//+------------------------------------------------------------------+ //| FrameQuad.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" //+------------------------------------------------------------------+ //| Single sprite animation frame class | //+------------------------------------------------------------------+ 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 public: //--- Constructors CFrameQuad() {;} CFrameQuad(const int id,CGCnvElement *element) : CFrame(id,0,0,0,0,element) { this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP; } //+------------------------------------------------------------------+
在类的私密部分,声明类成员变量,用于存储围绕所绘制造型的矩形的坐标和大小 — 这些是需保存的,被所绘制造型覆盖的背景部分图像区域的坐标和大小。
参数型构造函数接收欲创建对象的 ID,和指向欲创建对象图形元素的指针。 在方法参数中传递的 ID、默认坐标和帧大小参数、以及指向图形元素的指针,将传递给构造函数初始化清单中的父类构造函数。 在构造函数主体中,将绘制图形的锚点设置为“左上角”。 这是计算复制区域偏移所必需的。 有了这个值,X 和 Y 坐标锚点偏移就等于零。
由于我们在 CCanvas 类中有大量的绘制方法,所有在交互窗对象背景上绘制造型的相应方法都在类的公开部分声明,随后是恢复背景:
public: //--- Constructors CFrameQuad() {;} CFrameQuad(const int id,CGCnvElement *element) : CFrame(id,0,0,0,0,element) { this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP; } //+------------------------------------------------------------------+ //| Drawing primitives while saving and restoring the background | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Methods of drawing primitives without smoothing | //+------------------------------------------------------------------+ //--- Set the color of the dot with the specified coordinates bool SetPixelOnBG(const int x,const int y,const color clr,const uchar opacity=255,const bool redraw=false); //--- Draw a segment of a vertical line bool DrawLineVerticalOnBG(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a segment of a horizontal line bool DrawLineHorizontalOnBG(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a segment of a freehand line bool DrawLineOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a polyline bool DrawPolylineOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a polygon bool DrawPolygonOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a rectangle using two points bool DrawRectangleOnBG(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a circle bool DrawCircleOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a triangle bool DrawTriangleOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw an ellipse using two points bool DrawEllipseOnBG(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 //--- Draw an arc of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawArcOnBG(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawPieOnBG(const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Line color const color fill_clr, // Fill color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //+------------------------------------------------------------------+ //| Methods of drawing filled primitives without smoothing | //+------------------------------------------------------------------+ //--- Fill in the area bool FillOnBG(const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0, // Threshold const bool redraw=false); // Chart redraw flag //--- Draw a filled rectangle bool DrawRectangleFillOnBG(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a filled circle bool DrawCircleFillOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a filled triangle bool DrawTriangleFillOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a filled polygon bool DrawPolygonFillOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates bool DrawEllipseFillOnBG(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //+------------------------------------------------------------------+ //| Methods of drawing primitives using smoothing | //+------------------------------------------------------------------+ //--- Draw a point using AntiAliasing algorithm bool SetPixelAAOnBG(const double x, // Point X coordinate const double y, // Point Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false); // Chart redraw flag //--- Draw a segment of a freehand line using AntiAliasing algorithm bool DrawLineAAOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 segment of a freehand line using Wu algorithm bool DrawLineWuOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 //--- Draws a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND); // Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickVerticalOnBG(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint 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 horizontal segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickHorizontalOnBG(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint 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 //--- Draws a polyline using AntiAliasing algorithm bool DrawPolylineAAOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 //--- Draws a polyline using Wu algorithm bool DrawPolylineWuOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 polyline with a specified width consecutively using two antialiasing algorithms. //--- First, individual line segments are smoothed based on Bezier curves. //--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality bool DrawPolylineSmoothOnBG(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 polyline having a specified width using smoothing algorithm with the preliminary filtration bool DrawPolylineThickOnBG(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND); // Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a polygon using AntiAliasing algorithm bool DrawPolygonAAOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 polygon using Wu algorithm bool DrawPolygonWuOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 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 DrawPolygonSmoothOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 polygon having a specified width using smoothing algorithm with the preliminary filtration bool DrawPolygonThickOnBG(const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const 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 triangle using AntiAliasing algorithm bool DrawTriangleAAOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 triangle using Wu algorithm bool DrawTriangleWuOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 circle using AntiAliasing algorithm bool DrawCircleAAOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 circle using Wu algorithm bool DrawCircleWuOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 an ellipse by two points using AntiAliasing algorithm bool 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 //--- Draw an ellipse by two points using Wu algorithm bool 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 }; //+------------------------------------------------------------------+
这些方法的每一个实现,都与类似的绘图方法实现相似。 不过,几乎所有的绘制方法都有其固有的细微差别(两种相似的绘制方法的保存区域的大小可能因它们各自的特点而有所不同)。
我们来看看这些方法的实现。
设置指定坐标点颜色的方法:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
该方法的逻辑已在代码中进行了足够详细的注释。 我们来更通透地研究一下。 此处我们先把背景矩形区域左上边缘的 X、Y坐标保存到数组中,以便后续恢复绘制点之下的背景。 由于这只是一个点(一个像素的图像),保存区域的坐标与绘制点的坐标匹配,大小与单个像素的大小匹配,即 1 x 1。
接下来我们检查背景是否之前已保存(依据保存背景的数组大小非零)。 如果是,则恢复之前保存的交互窗对象背景(恢复区域的坐标和大小已在类变量中设置)。 背景恢复成功后,保存带有新像素点坐标的交互窗背景,并绘制像素点。 在类变量中保存新坐标,以及保存区域的大小和位移,以便后续恢复被新绘制的像素点擦除的背景。
绘制一条垂直线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a vertical line | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineVerticalOnBG(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=x; this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(y2-y1)+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 the background area with the 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.DrawLineVertical(x,y1,y2,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; } //+------------------------------------------------------------------+
在此计算勾勒造型的矩形坐标和大小,其与之前方法中的计算不尽相同。 这是很自然的,因为我们绘制的是一条宽度为一个像素的垂直线。 线的高度计算方法是线的两个 Y 坐标的最大值和最小值之间的差值。 保存区域的 Y 坐标则对应两个 Y 坐标的最小值(所绘线的顶点)。
绘制水平线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a horizontal line | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineHorizontalOnBG(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); 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=::fabs(x2-x1)+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 the background area with the 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.DrawLineHorizontal(x1,x2,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; } //+------------------------------------------------------------------+
在此,计算欲保存区域的坐标和大小与前面方法中的计算类似,只是此处是一条水平线,其高度等于一个像素,故只需计算保存区域的宽度和 X 坐标。
绘制手绘线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a freehand line | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawLine(x1,y1,x2,y2,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; } //+------------------------------------------------------------------+
此处,欲保存区域的坐标和大小是根据所绘制线的坐标计算出来的。
绘制折线的方法:
//+------------------------------------------------------------------+ //| Draw a polyline | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolylineOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; this.m_quad_height=(max_y_value-min_y_value)+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 the background area with the 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.DrawPolyline(array_x,array_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; } //+------------------------------------------------------------------+
此处计算欲保存区域坐标和大小背后的思路与上面研究的方法相同。 然而,执行则不同,因为在折线(以及许多其他造型)的情况下,坐标通过数组传递,而非变量的形式,因为不可能预先知道线打弯的数量,或坐标的数量,故无法在方法参数中传递。 这就是为什么在调用该方法之前,我们应该用 X 坐标和每条线打弯点的相应 Y 坐标填充两个数组。
在该方法中,我们用之前研究过的函数从数组中接收最大值和最小值,返回传递给函数的数组中的最小值或最大值。 所获值用于计算欲保存交互窗背景区域的坐标和大小。
其余的造型绘制方法无需平滑(只需关注计算保存区域坐标和大小):
//+------------------------------------------------------------------+ //| Draw the rectangle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; if(this.m_quad_width==0) this.m_quad_width=1; this.m_quad_height=(max_y_value-min_y_value)+1; if(this.m_quad_height==0) 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 the background area with the 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.DrawPolygon(array_x,array_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; } //+------------------------------------------------------------------+ //| Draw a rectangle using two points | //+------------------------------------------------------------------+ bool CFrameQuad::DrawRectangleOnBG(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawRectangle(x1,y1,x2,y2,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; } //+------------------------------------------------------------------+ //| Draw the circle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawCircleOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle int rd=(r>0 ? r : 1); this.m_quad_x=x-rd; this.m_quad_y=y-rd; double x2=x+rd; double y2=y+rd; if(this.m_quad_x<0) this.m_quad_x=0; if(this.m_quad_y<0) this.m_quad_y=0; //--- 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(x2-this.m_quad_x)+1); this.m_quad_height=int(::ceil(y2-this.m_quad_y)+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 the background area with the 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.DrawCircle(x,y,rd,clr,opacity); this.m_element.Update(redraw); this.m_anchor_last=TEXT_ANCHOR_CENTER; 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; } //+------------------------------------------------------------------+ //| Draw a triangle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawTriangleOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(::fmin(x1,x2),x3); this.m_quad_y=::fmin(::fmin(y1,y2),y3); int max_x=::fmax(::fmax(x1,x2),x3); int max_y=::fmax(::fmax(y1,y2),y3); //--- 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(max_x-this.m_quad_x)+1; this.m_quad_height=int(max_y-this.m_quad_y)+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 the background area with the 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.DrawTriangle(x1,y1,x2,y2,x3,y3,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; } //+------------------------------------------------------------------+ //| Draw an ellipse using two points | //+------------------------------------------------------------------+ bool CFrameQuad::DrawEllipseOnBG(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 { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawEllipse(x1,y1,x2,y2,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; } //+------------------------------------------------------------------+ //| Draw an arc of an ellipse inscribed in a rectangle | //| with the corners in (x1,y1) and (x2,y2). | //| The arc boundaries are cropped from the ellipse center | //| moving to two points with the coordinates of (x3,y3) and (x4,y4) | //+------------------------------------------------------------------+ bool CFrameQuad::DrawArcOnBG(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2)-1; this.m_quad_y=::fmin(y1,y2)-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=::fabs(x2-x1)+2; this.m_quad_height=::fabs(y2-y1)+2; //--- 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 the background area with the 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.DrawArc(x1,y1,x2,y2,x3,y3,x4,y4,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; } //+------------------------------------------------------------------+ //| Draw a filled sector of an ellipse inscribed in a rectangle | //| with the corners in (x1,y1) and (x2,y2). | //| The sector boundaries are cropped from the ellipse center, | //| moving to two points with the coordinates of (x3,y3) and (x4,y4) | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPieOnBG(const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Color const color fill_clr, // Fill color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2)-1; this.m_quad_y=::fmin(y1,y2)-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=::fabs(x2-x1)+2; this.m_quad_height=::fabs(y2-y1)+2; //--- 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 the background area with the 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.DrawPie(x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_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; } //+------------------------------------------------------------------+
我们来研究无需平滑的情况下绘制填充图元的方法。
区域填充方法:
//+------------------------------------------------------------------+ //| Fill in the area | //+------------------------------------------------------------------+ bool CFrameQuad::FillOnBG(const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0, // Threshold const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=0; this.m_quad_y=0; //--- 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=0; this.m_quad_height=0; //--- 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 the background area with the 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.Fill(x,y,clr,opacity,threshould); 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; } //+------------------------------------------------------------------+
由于该方法填充的是任意封闭区域,因此我们无法提前知道保存区域的大小。 因此,整个交互窗都应在此处保存。 为了实现这一点,我们将坐标和大小设置为零。 在这些值的情况下,保存图像矩形区域的方法会把整个交互窗背景保存到数组之中。
至于其余绘制填充图元的方法,它们计算坐标和保存区域大小与之前研究的简单无平滑图元的计算相匹配。 我们来看看这些方法:
//+------------------------------------------------------------------+ //| Draw a filled rectangle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawRectangleFillOnBG(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawRectangleFill(x1,y1,x2,y2,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; } //+------------------------------------------------------------------+ //| Draw a filled circle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawCircleFillOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle int rd=(r>0 ? r : 1); this.m_quad_x=x-rd; this.m_quad_y=y-rd; double x2=x+rd; double y2=y+rd; if(this.m_quad_x<0) this.m_quad_x=0; if(this.m_quad_y<0) this.m_quad_y=0; //--- 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(x2-this.m_quad_x)+1); this.m_quad_height=int(::ceil(y2-this.m_quad_y)+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 the background area with the 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.DrawCircleFill(x,y,rd,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; } //+------------------------------------------------------------------+ //| Draw a filled triangle | //+------------------------------------------------------------------+ bool CFrameQuad::DrawTriangleFillOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(::fmin(x1,x2),x3)-1; this.m_quad_y=::fmin(::fmin(y1,y2),y3)-1; int max_x=::fmax(::fmax(x1,x2),x3)+1; int max_y=::fmax(::fmax(y1,y2),y3)+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(max_x-this.m_quad_x)+1; this.m_quad_height=int(max_y-this.m_quad_y)+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 the background area with the 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.DrawTriangleFill(x1,y1,x2,y2,x3,y3,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; } //+------------------------------------------------------------------+ //| Draw a filled polygon | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonFillOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; this.m_quad_height=(max_y_value-min_y_value)+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 the background area with the 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.DrawPolygonFill(array_x,array_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; } //+------------------------------------------------------------------+ //| Draw a filled ellipse inscribed in a rectangle | //| with the given coordinates | //+------------------------------------------------------------------+ bool CFrameQuad::DrawEllipseFillOnBG(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false) // Chart redraw flag { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawEllipseFill(x1,y1,x2,y2,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; } //+------------------------------------------------------------------+
绘制平滑图元的方法。
运用抗锯齿算法绘制点的方法:
//+------------------------------------------------------------------+ //| Draw a point using AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::SetPixelAAOnBG(const double x, // Point X coordinate const double y, // Point Y coordinate 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_quad_x=x-1; if(this.m_quad_x<0) this.m_quad_x=0; this.m_quad_y=y-1; if(this.m_quad_y<0) this.m_quad_y=0; //--- 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=3; this.m_quad_height=3; //--- 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.SetPixelAA(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; } //+------------------------------------------------------------------+
此处计算欲保存区域坐标和大小与绘制无平滑点方法中的计算有别。 一个平滑点可以位于三个相邻的像素(共 9 个,即 3 x 3 个像素)上,因此保存区域的尺寸应为高 3 像素,宽 3 像素。 X 和 Y 坐标应分别位于点本身坐标左侧和上方 1 个像素的位置。 因此,围绕该点的保存区域矩形将与绘制点的所有边框有一个像素的边距,以防后者被平滑算法模糊,并在多个像素上绘制。 这将令我们能够在恢复因绘制平滑点而擦除的背景时,留有残影。
运用抗锯齿算法绘制手绘线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a freehand line | //| using AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineAAOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawLineAA(x1,y1,x2,y2,clr,opacity,style); 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; } //+------------------------------------------------------------------+
测试该方法发现绘制线的边缘不会模糊,因此计算欲保存区域大小与绘制无平滑线方法匹配。
使用 Wu 算法绘制一段手绘线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a freehand line using the | //| Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineWuOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; this.m_quad_height=::fabs(y2-y1)+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 the background area with the 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.DrawLineWu(x1,y1,x2,y2,clr,opacity,style); 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; } //+------------------------------------------------------------------+
出于同样的原因,此处的计算与之前的方法类似。
使用带有初步过滤的平滑算法绘制具有指定宽度的手绘线段的方法:
//+------------------------------------------------------------------+ //| Draw a segment of a freehand line having a specified width | //| using a smoothing algorithm | //| with the preliminary sorting | //+------------------------------------------------------------------+ bool CFrameQuad::DrawLineThickOnBG(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration's values { //--- 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_quad_x=::fmin(x1,x2)-correct; this.m_quad_y=::fmin(y1,y2)-correct; //--- 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=::fabs(x2-x1)+1+correct*2; this.m_quad_height=::fabs(y2-y1)+1+correct*2; //--- 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 the background area with the 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.DrawLineThick(x1,y1,x2,y2,size,clr,opacity,style,end_style); 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; } //+------------------------------------------------------------------+
在该方法中,计算欲保存区域与上面研究的不同。 由于采用这种平滑算法的线条具有可编辑的值,以及其末端外观,因此保存区域的宽度应考虑线条及其边缘的大小(宽度)(线条边缘可以倒圆角,因此线条大小 (length) 增加两个圆角半径,即线宽增加的值)。
使用带有初步过滤的平滑算法绘制具有指定宽度的垂直手绘线的方法:
//+---------------------------------------------------------------------+ //| Draw a vertical segment of a freehand line having a specified width | //| using a smoothing algorithm | //| with the preliminary sorting | //+---------------------------------------------------------------------+ bool CFrameQuad::DrawLineThickVerticalOnBG(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint 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 { //--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size and the type of its ends int correct_x=(int)::ceil((double)size/2.0); int correct_y=(end_style==LINE_END_BUTT ? 0 : correct_x); //--- Set the coordinates of the outlining rectangle this.m_quad_x=x-correct_x; this.m_quad_y=::fmin(y1,y2)-correct_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=size; this.m_quad_height=::fabs(y2-y1)+1+correct_y*2; //--- 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 the background area with the 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.DrawLineThickVertical(x,y1,y2,size,clr,opacity,style,end_style); 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; } //+------------------------------------------------------------------+
此处计算与之前讲述的方法相同。
其余绘制平滑和其他图元的方法:
//+-----------------------------------------------------------------------+ //| Draws a horizontal segment of a freehand line having a specified width| //| using a smoothing algorithm | //| with the preliminary sorting | //+-----------------------------------------------------------------------+ bool CFrameQuad::DrawLineThickHorizontalOnBG(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint 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 { //--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size and the type of its ends int correct_y=(int)::ceil((double)size/2.0); int correct_x=(end_style==LINE_END_BUTT ? 0 : correct_y); //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2)-correct_x; this.m_quad_y=y-correct_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=::fabs(x2-x1)+1+correct_x*2; this.m_quad_height=size; //--- 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 the background area with the 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.DrawLineThickHorizontal(x1,x2,y,size,clr,opacity,style,end_style); 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; } //+------------------------------------------------------------------+ //| Draw a polyline using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolylineAAOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; this.m_quad_height=(max_y_value-min_y_value)+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 the background area with the 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.DrawPolylineAA(array_x,array_y,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draws a polyline using Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolylineWuOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; this.m_quad_height=(max_y_value-min_y_value)+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 the background area with the 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.DrawPolylineWu(array_x,array_y,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draw a polyline with 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 the polyline segments | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolylineSmoothOnBG(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Chart redraw flag 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_quad_x=0; this.m_quad_y=0; //--- 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=this.m_element.Width(); this.m_quad_height=this.m_element.Height(); //--- 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 the background area with the 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.DrawPolylineSmooth(array_x,array_y,size,clr,opacity,tension,step,style,end_style); 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; } //+------------------------------------------------------------------+ //| Draw a polyline with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolylineThickOnBG(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration's values { //--- 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; this.m_quad_x=x-correct; this.m_quad_y=y-correct; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1+correct*2; this.m_quad_height=(max_y_value-min_y_value)+1+correct*2; //--- 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 the background area with the 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.DrawPolylineThick(array_x,array_y,size,clr,opacity,style,end_style); 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; } //+------------------------------------------------------------------+ //| Draw a polygon using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonAAOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; if(this.m_quad_width==0) this.m_quad_width=1; this.m_quad_height=(max_y_value-min_y_value)+1; if(this.m_quad_height==0) 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 the background area with the 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.DrawPolygonAA(array_x,array_y,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draw a polygon using Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonWuOnBG(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; 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) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1; if(this.m_quad_width==0) this.m_quad_width=1; this.m_quad_height=(max_y_value-min_y_value)+1; if(this.m_quad_height==0) 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 the background area with the 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.DrawPolygonWu(array_x,array_y,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draw a polygon with 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 the polyline segments. | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonSmoothOnBG(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Chart redraw flag 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_quad_x=0; this.m_quad_y=0; //--- 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=this.m_element.Width(); this.m_quad_height=this.m_element.Height(); //--- 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 the background area with the 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.DrawPolygonSmooth(array_x,array_y,size,clr,opacity,tension,step,style,end_style); 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; } //+------------------------------------------------------------------+ //| Draw a polygon with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ bool CFrameQuad::DrawPolygonThickOnBG(const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const 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 int x=0,y=0; if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y)) return false; this.m_quad_x=x-correct; this.m_quad_y=y-correct; //--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area) int max_x_value=0,min_x_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value)) return false; int max_y_value=0,min_y_value=0; if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value)) return false; this.m_quad_width=(max_x_value-min_x_value)+1+correct*2; this.m_quad_height=(max_y_value-min_y_value)+1+correct*2; //--- 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 the background area with the 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.DrawPolygonThick(array_x,array_y,size,clr,opacity,style,end_style); 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; } //+------------------------------------------------------------------+ //| Draw a triangle using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawTriangleAAOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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_quad_x=::fmin(::fmin(x1,x2),x3); this.m_quad_y=::fmin(::fmin(y1,y2),y3); int max_x=::fmax(::fmax(x1,x2),x3); int max_y=::fmax(::fmax(y1,y2),y3); //--- 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(max_x-this.m_quad_x)+1; this.m_quad_height=int(max_y-this.m_quad_y)+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 the background area with the 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.DrawTriangleAA(x1,y1,x2,y2,x3,y3,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draw a triangle using Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawTriangleWuOnBG(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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_quad_x=::fmin(::fmin(x1,x2),x3); this.m_quad_y=::fmin(::fmin(y1,y2),y3); int max_x=::fmax(::fmax(x1,x2),x3); int max_y=::fmax(::fmax(y1,y2),y3); //--- 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(max_x-this.m_quad_x)+1; this.m_quad_height=int(max_y-this.m_quad_y)+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 the background area with the 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.DrawTriangleWu(x1,y1,x2,y2,x3,y3,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| Draw a circle using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawCircleAAOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 double rd=(r>0 ? r : 1); this.m_quad_x=x-rd; this.m_quad_y=y-rd; double x2=x+rd; double y2=y+rd; if(this.m_quad_x<0) this.m_quad_x=0; if(this.m_quad_y<0) this.m_quad_y=0; //--- 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(x2-this.m_quad_x)+1); this.m_quad_height=int(::ceil(y2-this.m_quad_y)+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 the background area with the 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.DrawCircleAA(x,y,rd,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; } //+------------------------------------------------------------------+ //| Draw a circle using Wu algorithm | //+------------------------------------------------------------------+ bool CFrameQuad::DrawCircleWuOnBG(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 double rd=(r>0 ? r : 1); this.m_quad_x=x-rd; this.m_quad_y=y-rd; double x2=x+rd; double y2=y+rd; if(this.m_quad_x<0) this.m_quad_x=0; if(this.m_quad_y<0) this.m_quad_y=0; //--- 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(x2-this.m_quad_x)+1); this.m_quad_height=int(::ceil(y2-this.m_quad_y)+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 the background area with the 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.DrawCircleWu(x,y,rd,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; } //+------------------------------------------------------------------+ //| 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 { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2)-1; this.m_quad_y=::fmin(y1,y2)-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(::fabs(x2-x1)))+1; this.m_quad_height=int(::ceil(::fabs(y2-y1)))+1; if(this.m_quad_width<3) this.m_quad_width=3; if(this.m_quad_height<3) this.m_quad_height=3; //--- 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 the background area with the 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.DrawEllipseAA(x1,y1,x2,y2,clr,opacity,style); 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; } //+------------------------------------------------------------------+ //| 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 { //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); this.m_quad_y=::fmin(y1,y2); //--- 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=::fabs(x2-x1)+1; if(this.m_quad_width<3) this.m_quad_width=3; this.m_quad_height=::fabs(y2-y1)+1; if(this.m_quad_height<3) this.m_quad_height=3; //--- 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 the background area with the 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.DrawEllipseWu(x1,y1,x2,y2,clr,opacity,style); 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; } //+------------------------------------------------------------------+
此处,所有方法保存的背景区域算法与上述方法中的算法大致相同。
在椭圆绘制方法(DrawEllipseAAOnBG 和 DrawEllipseWuOnBG)中,如果绘制椭圆的矩形小于三个像素,则不绘制任何造型。 因此,此处的计算包含对小于三个像素大小的检查。 我还未决定,是否是我的错误,还是 CCanvas 类方法的一个特性。 我希望以后能澄清这一点。
我已经开发完毕目前需要的动画框对象的所有类。
现在是时候创建一个类来存储、访问和管理所创建动画框对象了。
该类将包含两个(目前)列表,存储所创建动画框对象(文本和矩形对象),以及创建和管理新对象的方法。 随后,该类将存储属于一个交互窗的所有动画对象。 因此,每个交互窗都有自己的一组动画对象,这些对象可以动态创建,并添加到交互窗动画列表中。
交互窗动画类
在 \MQL5\Include\DoEasy\Objects\Graph\Animations\ 里,创建 CAnimations 类的新 Animations.mqh 文件。
只有新创建的基准动画框对象的衍生后代类文件才可包含在类文件之中,而类本身应该是 CObject 标准库基准对象的衍生后代:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CAnimations : public CObject { }
在类的私密部分,声明指向欲创建动画对象的图形元素对象指针、存储两种类型动画框对象的两个列表、以及用于返回指定对象存在于列表中的标志的方法、以及返回指向现有动画框对象的指针方法、如若它不存在于列表中,则初步创建它:
//+------------------------------------------------------------------+ //| 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 rectangle animation 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 soutce,const int id,const ENUM_ANIMATION_FRAME_TYPE frame_type,const bool create_new); public:
所有方法均在下面予以研究。
在类的公开部分,声明创建和操控列表中对象的方法,以及绘制图元,同时保存和恢复背景的方法:
public: CAnimations(CGCnvElement *element); CAnimations(){;} //--- Create a new (1) rectangle and (2) text animation frame object CFrame *CreateNewFrameText(const int id); CFrame *CreateNewFrameQuad(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 and (2) rectangle animation frames CArrayObj *GetListFramesText(void) { return &this.m_list_frames_text; } CArrayObj *GetListFramesQuad(void) { return &this.m_list_frames_quad; } //+------------------------------------------------------------------+ //| The methods of drawing, while saving and restoring the background| //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Display a text on the background | //+------------------------------------------------------------------+ bool TextOnBG(const int id, const string text, const int x, const int y, const ENUM_TEXT_ANCHOR anchor, const color clr, const uchar opacity, const bool create_new=true, const bool redraw=false); //+------------------------------------------------------------------+ //| Methods of drawing primitives without smoothing | //+------------------------------------------------------------------+ //--- Set the color of the dot with the specified coordinates bool SetPixelOnBG(const int id,const int x,const int y,const color clr,const uchar opacity=255,const bool create_new=true,const bool redraw=false); //--- Draw a segment of a vertical line bool DrawLineVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a segment of a horizontal line bool DrawLineHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a segment of a freehand line bool DrawLineOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a polyline bool DrawPolylineOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a polygon bool DrawPolygonOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a rectangle using two points bool DrawRectangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a circle bool DrawCircleOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a triangle bool DrawTriangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw an ellipse using two points bool DrawEllipseOnBG(const int id, // Frame ID 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 create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw an arc of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawArcOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawPieOnBG(const int id, // Frame ID const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Color const color fill_clr, // Fill color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //+------------------------------------------------------------------+ //| Methods of drawing filled primitives without smoothing | //+------------------------------------------------------------------+ //--- Fill in the area bool FillOnBG(const int id, // Frame ID const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0, // Threshold const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled rectangle bool DrawRectangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled circle bool DrawCircleFillOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled triangle bool DrawTriangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled polygon bool DrawPolygonFillOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates bool DrawEllipseFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false); // Chart redraw flag //+------------------------------------------------------------------+ //| Methods of drawing primitives using smoothing | //+------------------------------------------------------------------+ //--- Draw a point using AntiAliasing algorithm bool SetPixelAAOnBG(const int id, // Frame ID const double x, // Point X coordinate const double y, // Point Y coordinate 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 segment of a freehand line using AntiAliasing algorithm bool DrawLineAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 segment of a freehand line using Wu algorithm bool DrawLineWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 //--- Draws a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND); // Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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 horizontal segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration bool DrawLineThickHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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 //--- Draws a polyline using AntiAliasing algorithm bool DrawPolylineAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 //--- Draws a polyline using Wu algorithm bool DrawPolylineWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 polyline with a specified width consecutively using two antialiasing algorithms. //--- First, individual line segments are smoothed based on Bezier curves. //--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality bool DrawPolylineSmoothOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 polyline having a specified width using smoothing algorithm with the preliminary filtration bool DrawPolylineThickOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND); // Line style is one of the ENUM_LINE_END enumeration's values //--- Draw a polygon using AntiAliasing algorithm bool DrawPolygonAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 polygon using Wu algorithm bool DrawPolygonWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 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 DrawPolygonSmoothOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 polygon having a specified width using smoothing algorithm with the preliminary filtration bool DrawPolygonThickOnBG(const int id, // Frame ID const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const 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 //--- Draw a triangle using AntiAliasing algorithm bool DrawTriangleAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 triangle using Wu algorithm bool DrawTriangleWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 circle using AntiAliasing algorithm bool DrawCircleAAOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 circle using Wu algorithm bool DrawCircleWuOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 an ellipse by two points using AntiAliasing algorithm bool DrawEllipseAAOnBG(const int id, // Frame ID 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 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 an ellipse by two points using Wu algorithm bool DrawEllipseWuOnBG(const int id, // Frame ID 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 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 }; //+------------------------------------------------------------------+
我们来研究一下所声明方法的实现。
参数型构造函数:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CAnimations::CAnimations(CGCnvElement *element) { this.m_element=element; } //+------------------------------------------------------------------+
在参数中传递的指向图形元素对象的指针值,会赋值给 m_element 指针。
该方法返回按类型和 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() : 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; 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; } //+------------------------------------------------------------------+
方法逻辑在代码注释中已有详细讲述,无需赘言。
该方法返回标志,指示列表中是否含有指定类型和 ID 的框对象:
//+-----------------------------------------------------------------------+ //| Return the flag indicating the presence of the animation frame object | //| with the specified type and ID | //+-----------------------------------------------------------------------+ bool CAnimations::IsPresentFrame(const ENUM_ANIMATION_FRAME_TYPE frame_type,const int id) { return(this.GetFrame(frame_type,id)!=NULL); } //+------------------------------------------------------------------+
该方法返回调用上述 GetFrame() 方法的 bool 结果。 如果 GetFrame() 方法返回非 NULL 结果(所需对象存在于列表中),则该方法返回 true,否则 — false。
该方法创建一个新的文本动画框对象:
//+------------------------------------------------------------------+ //| Create a new text animation frame object | //+------------------------------------------------------------------+ CFrame *CAnimations::CreateNewFrameText(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_TEXT,id)) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST),(string)id); return NULL; } //--- Create a new text animation frame object with the specified ID CFrame *frame=new CFrameText(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_text.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; } //+------------------------------------------------------------------+
代码注释中完整描述了方法逻辑。
的方法该建一个新的矩形动画框对象:
//+------------------------------------------------------------------+ //| Create a new rectangle animation frame object | //+------------------------------------------------------------------+ CFrame *CAnimations::CreateNewFrameQuad(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_QUAD,id)) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST),(string)id); return NULL; } //--- Create a new rectangle animation frame object with the specified ID CFrame *frame=new CFrameQuad(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_quad.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; //--- 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 rectangle 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 rectangle animation frame object (pointer to the created object) return this.CreateNewFrameQuad(id); //--- In the remaining cases, return NULL default: return NULL; } } //+------------------------------------------------------------------+
方法逻辑已在代码注释中描述。 如果我们需要操控一个动画框,我们可以预先创建它,获取指向它的指针,并管理获取的对象。 如果我们需要动态创建对象,这个方法允许我们先创建一个新对象(前提是没有指定 ID 的对象),并返回指向它的指针。 因此,可相应地安排对象的动态创建,立即获得指向它的指针,并对其进行管理。
操控动画框对象的方法。
在背景上显示文本,同时保存和恢复背景的方法:
//+--------------------------------------------------------------------------------+ //| Display the text on the background, while saving and restoring the background | //+--------------------------------------------------------------------------------+ bool CAnimations::TextOnBG(const int id, const string text, const int x, const int y, const ENUM_TEXT_ANCHOR anchor, const color clr, const uchar opacity, const bool create_new=true, const bool redraw=false) { CFrameText *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_TEXT,create_new); if(frame==NULL) return false; return frame.TextOnBG(text,x,y,anchor,clr,opacity,redraw); } //+------------------------------------------------------------------+
该方法接收对象 ID、欲显示的文本参数(文本本身、X 和 Y 坐标、锚点、颜色和不透明度)、如果在列表中不存在这个对象的 ID,则指示需要创建含有指定 ID 新对象的标志,以及图表重绘标志。
接下来,获取指向所需对象的指针(如果不存在,则创建该对象)。 如果获取指针失败,则返回 false。
如果收到指针,则返回所获取文本动画框对象的 TextOnBG() 方法的结果。
设置指定坐标点颜色的方法:
//+------------------------------------------------------------------+ //| Set the color of the dot with the specified coordinates | //+------------------------------------------------------------------+ bool CAnimations::SetPixelOnBG(const int id,const int x,const int y,const color clr,const uchar opacity=255,const bool create_new=true,const bool redraw=false) { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.SetPixelOnBG(x,y,clr,opacity,redraw); } //+------------------------------------------------------------------+
方法逻辑与上面研究过的方法之一雷同。 该方法接收对象 ID、绘制的形状 X 和 Y 坐标、颜色和不透明度、如若这个 ID 的对象不在列表中,则指示需要创建指定 ID 新对象的标志,以及 图表重绘标志。
接下来,获取指向所需对象的指针(如果不存在,则创建该对象)。 如果获取指针失败,则返回 false。
如果收到指针,则返回所获取动画矩形框对象的 SetPixelOnBG() 方法的结果。
绘制图元的其它方法。
其余造型绘制方法的逻辑与上面研究的方法的逻辑雷同。 我们来看看它们的清单:
//+------------------------------------------------------------------+ //| Draw a segment of a vertical line | //+------------------------------------------------------------------+ bool CAnimations::DrawLineVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineVerticalOnBG(x,y1,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a segment of a horizontal line | //+------------------------------------------------------------------+ bool CAnimations::DrawLineHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineHorizontalOnBG(x1,x2,y,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a segment of a freehand line | //+------------------------------------------------------------------+ bool CAnimations::DrawLineOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineOnBG(x1,y1,x2,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a polyline | //+------------------------------------------------------------------+ bool CAnimations::DrawPolylineOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolylineOnBG(array_x,array_y,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw the rectangle | //+------------------------------------------------------------------+ bool CAnimations::DrawPolygonOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonOnBG(array_x,array_y,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a rectangle using two points | //+------------------------------------------------------------------+ bool CAnimations::DrawRectangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawRectangleOnBG(x1,y1,x2,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw the circle | //+------------------------------------------------------------------+ bool CAnimations::DrawCircleOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawCircleOnBG(x,y,r,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a triangle | //+------------------------------------------------------------------+ bool CAnimations::DrawTriangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawTriangleOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw an ellipse using two points | //+------------------------------------------------------------------+ bool CAnimations::DrawEllipseOnBG(const int id, // Frame ID 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 create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawEllipseOnBG(x1,y1,x2,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw an arc of an ellipse inscribed in a rectangle | //| with the corners in (x1,y1) and (x2,y2). | //| The arc boundaries are cropped from the ellipse center | //| moving to two points with the coordinates of (x3,y3) and (x4,y4) | //+------------------------------------------------------------------+ bool CAnimations::DrawArcOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawArcOnBG(x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a filled sector of an ellipse inscribed in a rectangle | //| with the corners in (x1,y1) and (x2,y2). | //| The sector boundaries are cropped from the ellipse center, | //| moving to two points with the coordinates of (x3,y3) and (x4,y4) | //+------------------------------------------------------------------+ bool CAnimations::DrawPieOnBG(const int id, // Frame ID const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Color const color fill_clr, // Fill color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPieOnBG(x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Fill in the area | //+------------------------------------------------------------------+ bool CAnimations::FillOnBG(const int id, // Frame ID const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0, // Threshold const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.FillOnBG(x,y,clr,opacity,threshould,redraw); } //+------------------------------------------------------------------+ //| Draw a filled rectangle | //+------------------------------------------------------------------+ bool CAnimations::DrawRectangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawRectangleFillOnBG(x1,y1,x2,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a filled circle | //+------------------------------------------------------------------+ bool CAnimations::DrawCircleFillOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawCircleFillOnBG(x,y,r,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a filled triangle | //+------------------------------------------------------------------+ bool CAnimations::DrawTriangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawTriangleFillOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a filled polygon | //+------------------------------------------------------------------+ bool CAnimations::DrawPolygonFillOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonFillOnBG(array_x,array_y,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a filled ellipse inscribed in a rectangle | //| with the given coordinates | //+------------------------------------------------------------------+ bool CAnimations::DrawEllipseFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawEllipseFillOnBG(x1,y1,x2,y2,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a point using AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::SetPixelAAOnBG(const int id, // Frame ID const double x, // Point X coordinate const double y, // Point Y coordinate 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.SetPixelAAOnBG(x,y,clr,opacity,redraw); } //+------------------------------------------------------------------+ //| Draw a segment of a freehand line using the | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawLineAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineAAOnBG(x1,y1,x2,y2,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a segment of a freehand line using the | //| Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawLineWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineWuOnBG(x1,y1,x2,y2,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a segment of a freehand line having a specified width | //| using a smoothing algorithm | //| with the preliminary sorting | //+------------------------------------------------------------------+ bool CAnimations::DrawLineThickOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration's values { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineThickOnBG(x1,y1,x2,y2,size,clr,opacity,redraw,style,end_style); } //+---------------------------------------------------------------------+ //| Draw a vertical segment of a freehand line having a specified width | //| using a smoothing algorithm | //| with the preliminary sorting | //+---------------------------------------------------------------------+ bool CAnimations::DrawLineThickVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineThickVerticalOnBG(x,y1,y2,size,clr,opacity,redraw,style,end_style); } //+-----------------------------------------------------------------------+ //| Draws a horizontal segment of a freehand line having a specified width| //| using a smoothing algorithm | //| with the preliminary sorting | //+-----------------------------------------------------------------------+ bool CAnimations::DrawLineThickHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawLineThickHorizontalOnBG(x1,x2,y,size,clr,opacity,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a polyline using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawPolylineAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolylineAAOnBG(array_x,array_y,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draws a polyline using Wu algorithm | //+------------------------------------------------------------------+ //--- bool CAnimations::DrawPolylineWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolylineWuOnBG(array_x,array_y,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a polyline with 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 | //| made of the polyline segments is applied | //+------------------------------------------------------------------+ bool CAnimations::DrawPolylineSmoothOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolylineSmoothOnBG(array_x,array_y,size,clr,opacity,tension,step,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a polyline with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ bool CAnimations::DrawPolylineThickOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration's values { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolylineThickOnBG(array_x,array_y,size,clr,opacity,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a polygon using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawPolygonAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonAAOnBG(array_x,array_y,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a polygon using Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawPolygonWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonWuOnBG(array_x,array_y,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a polygon with 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 the polyline segments. | //+------------------------------------------------------------------+ bool CAnimations::DrawPolygonSmoothOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonSmoothOnBG(array_x,array_y,size,clr,opacity,tension,step,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a polygon with a specified width using | //| a smoothing algorithm with the preliminary sorting | //+------------------------------------------------------------------+ //--- Draw a polygon having a specified width using smoothing algorithm with the preliminary filtration bool CAnimations::DrawPolygonThickOnBG(const int id, // Frame ID const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonThickOnBG(array_x,array_y,size,clr,opacity,redraw,style,end_style); } //+------------------------------------------------------------------+ //| Draw a triangle using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawTriangleAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawTriangleAAOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a triangle using Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawTriangleWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawTriangleWuOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a circle using | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawCircleAAOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawCircleAAOnBG(x,y,r,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw a circle using Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawCircleWuOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawCircleWuOnBG(x,y,r,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw an ellipse using two points while applying | //| AntiAliasing algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawEllipseAAOnBG(const int id, // Frame ID 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 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawEllipseAAOnBG(x1,y1,x2,y2,clr,opacity,redraw,style); } //+------------------------------------------------------------------+ //| Draw an ellipse using two points | //| using Wu algorithm | //+------------------------------------------------------------------+ bool CAnimations::DrawEllipseWuOnBG(const int id, // Frame ID 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 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 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawEllipseWuOnBG(x1,y1,x2,y2,clr,opacity,redraw,style); } //+------------------------------------------------------------------+
新创建的动画对象类应该是交互窗对象的组成部分。 如此,每个交互窗都有自己的创建图像的方法。
打开交互窗对象类 \MQL5\Include\DoEasy\Objects\Graph\Form.mqh 的文件,并添加必要的改进。
包含动画对象类的文件:
//+------------------------------------------------------------------+ //| Form.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 "GCnvElement.mqh" #include "ShadowObj.mqh" #include "Animations\Animations.mqh" //+------------------------------------------------------------------+
从清单里删除像素复制器对象类(我已将其移至另一个文件):
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GCnvElement.mqh" #include "ShadowObj.mqh" #include "Animations\Animations.mqh" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CPixelCopier : public CObject { private: ... } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+
在类的私密部分,替代像素复制器清单
CArrayObj m_list_pc_obj; // List of pixel copier objects
声明指向动画类对象的指针:
//+------------------------------------------------------------------+ //| Form.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 "GCnvElement.mqh" #include "ShadowObj.mqh" #include "Animations\Animations.mqh" //+------------------------------------------------------------------+ //| 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); //--- Return the name of the dependent object string CreateNameDependentObject(const string base_name) const { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name; } //--- Create a new graphical object CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity); //--- Create a shadow object void CreateShadowObj(const color colour,const uchar opacity); public:
从私密部分移除已经不必要的 IsPresentPC() 方法的声明,以及代码清单中的实现:
//--- Create a shadow object void CreateShadowObj(const color colour,const uchar opacity); //--- Return the flag indicating the presence of the copier object with the specified ID in the list bool IsPresentPC(const int id); public:
从类的公开部分删除已经不需要的方法:
//--- Return (1) itself, the list of (2) attached objects, (3) pixel copier objects and (4) the shadow object CForm *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list_elements; } CArrayObj *GetListPC(void) { return &this.m_list_pc_obj; } CGCnvElement *GetShadowObj(void) { return this.m_shadow_obj; }
并添加返回指向动画对象的指针,以及文本和矩形动画框列表的新方法:
CGCnvElement *GetShadowObj(void) { return this.m_shadow_obj; } //--- Return the pointer to (1) the animation object, the list of (2) text and (3) rectangle animation frames CAnimations *GetAnimationsObj(void) { return this.m_animations; } CArrayObj *GetListFramesText(void) { return(this.m_animations!=NULL ? this.m_animations.GetListFramesText() : NULL); } CArrayObj *GetListFramesQuad(void) { return(this.m_animations!=NULL ? this.m_animations.GetListFramesQuad() : NULL); } //--- Set the form (1) color scheme and (2) style
删除创建新像素复制对象的方法声明:
//--- Create a new pixel copier object CPixelCopier *CreateNewPixelCopier(const int id,const int x_coord,const int y_coord,const int width,const int height); //--- Draw an object shadow
在类主体之外编写的方法实现也一并被删除。
在类的公开部分中操控图像像素的方法模块中,编写创建动画框对象、返回指向创建对象的指针、以及保存和恢复背景的新方法:
//+------------------------------------------------------------------+ //| Methods of working with image pixels | //+------------------------------------------------------------------+ //--- Create a new (1) rectangle and (2) text animation frame object bool CreateNewFrameText(const int id,const int x_coord,const int y_coord,const string text) { return(this.m_animations!=NULL ? this.m_animations.CreateNewFrameText(id)!=NULL : false); } bool CreateNewFrameQuad(const int id,const int x_coord,const int y_coord,const int width,const int height) { return(this.m_animations!=NULL ? this.m_animations.CreateNewFrameQuad(id)!=NULL : false); } //--- Return the frame object of the (1) text and (2) rectangle animation by ID CFrame *GetFrameText(const int id) { return(this.m_animations!=NULL ? this.m_animations.GetFrame(ANIMATION_FRAME_TYPE_TEXT,id) : NULL); } CFrame *GetFrameQuad(const int id) { return(this.m_animations!=NULL ? this.m_animations.GetFrame(ANIMATION_FRAME_TYPE_QUAD,id) : NULL); } //--- Display the text on the background while saving and restoring the background bool TextOnBG(const int id, const string text, const int x, const int y, const ENUM_TEXT_ANCHOR anchor, const color clr, const uchar opacity=255, const bool create_new=true, const bool redraw=false) { return(this.m_animations!=NULL ? this.m_animations.TextOnBG(id,text,x,y,anchor,clr,opacity,create_new,redraw) : false); } //--- Set the color of the point with the specified coordinates while saving and restoring the background bool SetPixelOnBG(const int id,const int x,const int y,const color clr,const uchar opacity=255,const bool create_new=true,const bool redraw=false) { return(this.m_animations!=NULL ? this.m_animations.SetPixelOnBG(id,x,y,clr,opacity,create_new,redraw) : false); } //--- Draw a segment of a vertical line bool DrawLineVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawLineVerticalOnBG(id,x,y1,y2,clr,opacity,create_new,redraw) : false); } //--- Draw a segment of a horizontal line while saving and restoring the background bool DrawLineHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawLineHorizontalOnBG(id,x1,x2,y,clr,opacity,create_new,redraw) : false); } //--- Draw a segment of a freehand line while saving and restoring the background bool DrawLineOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawLineOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); } //--- Draw a polyline while saving and restoring the background bool DrawPolylineOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawPolylineOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false); } //--- Draw a polygon while saving and restoring the background bool DrawPolygonOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawPolygonOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false); } //--- Draw a rectangle by two points while saving and restoring the background bool DrawRectangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawRectangleOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); } //--- Draw a circle while saving and restoring the background bool DrawCircleOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawCircleOnBG(id,x,y,r,clr,opacity,create_new,redraw) : false); } //--- Draw a triangle while saving and restoring the background bool DrawTriangleOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawTriangleOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw) : false); } //--- Draw an ellipse by two points while saving and restoring the background bool DrawEllipseOnBG(const int id, // Frame ID 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 create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawEllipseOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); } //--- Draw an arc of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2) while saving and restoring the background. //--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawArcOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawArcOnBG(id,x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity,create_new,redraw) : false); } //--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2) while saving and restoring the background. //--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) bool DrawPieOnBG(const int id, // Frame ID const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Color const color fill_clr, // Fill 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.DrawPieOnBG(id,x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_clr,opacity,create_new,redraw) : false); } //--- Fill the area while saving and restoring the background bool FillOnBG(const int id, // Frame ID const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0, // Threshold const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.FillOnBG(id,x,y,clr,opacity,threshould,create_new,redraw) : false); } //--- Draw a filled rectangle while saving and restoring the background bool DrawRectangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawRectangleFillOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); } //--- Draw a filled circle while saving and restoring the background bool DrawCircleFillOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawCircleFillOnBG(id,x,y,r,clr,opacity,create_new,redraw) : false); } //--- Draw a filled triangle while saving and restoring the background bool DrawTriangleFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawTriangleFillOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw) : false); } //--- Draw a filled polygon while saving and restoring the background bool DrawPolygonFillOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawPolygonFillOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false); } //--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates while saving and restoring the background bool DrawEllipseFillOnBG(const int id, // Frame ID const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false) // Chart redraw flag { return(this.m_animations!=NULL ? this.m_animations.DrawEllipseFillOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); } //--- Draw a point using AntiAliasing algorithm while saving and restoring the background bool SetPixelAAOnBG(const int id, // Frame ID const double x, // Point X coordinate const double y, // Point Y coordinate 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.SetPixelAAOnBG(id,x,y,clr,opacity,create_new,redraw) : false); } //--- Draw a segment of a freehand line using AntiAliasing algorithm while saving and restoring the background bool DrawLineAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawLineAAOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false); } //--- Draw a segment of a freehand line using Wu algorithm while saving and restoring the background bool DrawLineWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawLineWuOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false); } //--- Draw a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration while saving and restoring the background bool DrawLineThickOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value 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.DrawLineThickOnBG(id,x1,y1,x2,y2,size,clr,opacity,create_new,redraw,style,end_style) : false); } //--- Draw a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration while saving and restoring the background bool DrawLineThickVerticalOnBG(const int id, // Frame ID const int x, // Segment X coordinate const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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.DrawLineThickVerticalOnBG(id,x,y1,y2,size,clr,opacity,create_new,redraw,style,end_style) : false); } //--- Draw a horizontal segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration while saving and restoring the background bool DrawLineThickHorizontalOnBG(const int id, // Frame ID const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint 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.DrawLineThickHorizontalOnBG(id,x1,x2,y,size,clr,opacity,create_new,redraw,style,end_style) : false); } //--- Draw a polyline using AntiAliasing algorithm while saving and restoring the background bool DrawPolylineAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawPolylineAAOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); } //--- Draw a polyline using Wu algorithm while saving and restoring the background bool DrawPolylineWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawPolylineWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); } //--- Draw a polyline with a specified width consecutively using two smoothing algorithms while saving and restoring the background. //--- First, individual line segments are smoothed based on Bezier curves. //--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality bool DrawPolylineSmoothOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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.DrawPolylineSmoothOnBG(id,array_x,array_y,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false); } //--- Draw a polyline having a specified width using smoothing algorithm with the preliminary filtration while saving and restoring the background bool DrawPolylineThickOnBG(const int id, // Frame ID const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const bool create_new=true, // New object creation flag const bool redraw=false, // Chart redraw flag const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value 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.DrawPolylineThickOnBG(id,array_x,array_y,size,clr,opacity,create_new,redraw,style,end_style) : false); } //--- Draw a polygon using AntiAliasing algorithm while saving and restoring the background bool DrawPolygonAAOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawPolygonAAOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); } //--- Draw a polygon using Wu algorithm while saving and restoring the background bool DrawPolygonWuOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawPolygonWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); } //--- Draw a polygon with a specified width consecutively using two smoothing algorithms while saving and restoring the background. //--- 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 DrawPolygonSmoothOnBG(const int id, // Frame ID int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const 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.DrawPolygonSmoothOnBG(id,array_x,array_y,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false); } //--- Draw a polygon of a specified width using a smoothing algorithm with the preliminary filtration while saving and restoring the background bool DrawPolygonThickOnBG(const int id, // Frame ID const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawPolygonThickOnBG(id,array_x,array_y,size,clr,opacity,create_new,redraw,style,end_style) : false); } //--- Draw a triangle using AntiAliasing algorithm while saving and restoring the background bool DrawTriangleAAOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawTriangleAAOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw,style) : false); } //--- Draw a triangle using Wu algorithm while saving and restoring the background bool DrawTriangleWuOnBG(const int id, // Frame ID const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawTriangleWuOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw,style) : false); } //--- Draw a circle using AntiAliasing algorithm while saving and restoring the background bool DrawCircleAAOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawCircleAAOnBG(id,x,y,r,clr,opacity,create_new,redraw,style) : false); } //--- Draw a circle using Wu algorithm while saving and restoring the background bool DrawCircleWuOnBG(const int id, // Frame ID const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const 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.DrawCircleWuOnBG(id,x,y,r,clr,opacity,create_new,redraw,style) : false); } //--- Draw an ellipse by two points using AntiAliasing algorithm while saving and restoring the background bool DrawEllipseAAOnBG(const int id, // Frame ID 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 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.DrawEllipseAAOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false); } //--- Draw an ellipse by two points using Wu algorithm while saving and restoring the background bool DrawEllipseWuOnBG(const int id, // Frame ID 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 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.DrawEllipseWuOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false); } //+------------------------------------------------------------------+
所有方法均从上面创建的 CAnimations 动画对象返回对应方法的结果。
在类的析构函数中,删除动画对象:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CForm::~CForm() { if(this.m_shadow_obj!=NULL) delete this.m_shadow_obj; if(this.m_animations!=NULL) delete this.m_animations; } //+------------------------------------------------------------------+
在初始化方法中,新建一个动画对象:
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CForm::Initialize(void) { this.m_list_elements.Clear(); this.m_list_elements.Sort(); this.m_shadow_obj=NULL; this.m_shadow=false; this.m_frame_width_right=2; this.m_frame_width_left=2; this.m_frame_width_top=2; this.m_frame_width_bottom=2; this.m_animations=new CAnimations(CGCnvElement::GetObject()); } //+------------------------------------------------------------------+
现在,在创建新交互窗对象时会自动创建一个新的动画对象。 我们可以动态地向动画对象添加新的动画框或创建预定义的交互窗。 直至程序运行完成后,动画对象被注销,从而避免内存泄漏。
我们可以准备测试创建的类了。
我们已有一个测试 EA,其中绘制了四种造型。 当我们单击第四个(底部)交互窗时,H-Gradient 标签移动(H - 用于水平填充类型)。 现在创建和移动此标签是作为文本动画框对象。 第三个交互窗(垂直填充)显示各种绘制的造型。 在绘制造型之前,我们首先显示交互窗文本作为文本动画框。 并在其上方绘制造型。 通过按键切换绘制图形的类型,同时文本显示选定的绘制图形。 每次鼠标单击造型,都会更改所绘制造型锚点的坐标。
测试
为了执行测试,我们借用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part79\ 中,命名为 TestDoEasyPart79.mq5。
在全局变量区域,输入指定绘制图形初始坐标的宏替换,声明保存绘制图形不同点坐标的变量(共有5个 点),以及它们的变化值:
//+------------------------------------------------------------------+ //| TestDoEasyPart79.mq5 | //| 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" //--- includes #include <Arrays\ArrayObj.mqh> #include <DoEasy\Services\Select.mqh> #include <DoEasy\Objects\Graph\Form.mqh> //--- defines #define FORMS_TOTAL (4) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape //--- input parameters sinput bool InpMovable = true; // Movable forms flag sinput ENUM_INPUT_YES_NO InpUseColorBG = INPUT_YES; // Use chart background color to calculate shadow color sinput color InpColorForm3 = clrCadetBlue; // Third form shadow color (if not background color) //--- global variables CArrayObj list_forms; color array_clr[]; int nx1=0, ny1=0, nx2=0, ny2=0, nx3=0, ny3=0, nx4=0, ny4=0, nx5=0, ny5=0; int coordX1=START_X+nx1; int coordY1=START_Y+ny1; int coordX2=START_X+nx2*2; int coordY2=START_Y+ny2*2; int coordX3=START_X+nx3*3; int coordY3=START_Y+ny3*3; int coordX4=START_X+nx4*4; int coordY4=START_Y+ny4*4; int coordX5=START_X+nx5*5; int coordY5=START_Y+ny5*5; double RD=1; //+------------------------------------------------------------------+
在 OnInit() 处理程序里,创建最后两个交互窗的模块中,利用文本动画对象创建文本:
//--- 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()); //--- 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,TEXT_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()); //--- 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,TEXT_ANCHOR_CENTER,C'211,233,149',255,true,true); } //--- Add objects to the list
我事先已在此处创建了这些文本对象。 绘制造型可动态创建 — 点击交互窗区域。
为了处理击键,请将以下代码模块添加到 OnChartEvent() 处理程序里:
//--- Drawing mode depending on the pressed key static ENUM_FIGURE_TYPE figure_type_prev=WRONG_VALUE; static ENUM_FIGURE_TYPE figure_type=figure_type_prev; string figure=FigureTypeDescription(figure_type); //--- 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 and display the text describing the drawn shape type nx1=ny1=nx2=ny2=nx3=ny3=nx4=ny4=nx5=ny5=0; 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; } } //--- If clicking an object
稍微改变交互窗点击处理模块中的代码 — 现在将用被点击交互窗的文本动画对象显示文本:
//--- If clicking an object if(id==CHARTEVENT_OBJECT_CLICK) { //--- If the clicked object belongs to the EA if(StringFind(sparam,MQLInfoString(MQL_PROGRAM_NAME))==0) { //--- Get the object ID from it int form_id=(int)StringToInteger(StringSubstr(sparam,StringLen(sparam)-1))-1; //--- Find this form object in the loop by all forms created in the EA for(int i=0;i<list_forms.Total();i++) { CForm *form=list_forms.At(i); if(form==NULL) continue; //--- If the clicked object has the ID of 2 and the form has the same ID if(form_id==2 && form.ID()==2) { //--- Handle clicking the form - draw the corresponding shape FigureProcessing(form,figure_type); } //--- If the clicked object has the ID of 3 and the form has the same ID if(form_id==3 && form.ID()==3) { ////--- Get the anchor point of the last drawn text ENUM_TEXT_ANCHOR anchor=form.TextAnchor(); ////--- Get the coordinates of the last drawn text int text_x=form.TextLastX(); int text_y=form.TextLastY(); //--- Set the text anchor initial point (0 = LEFT_TOP) out of nine possible ones static int n=0; //--- Depending on the n variable, set the new text anchor point switch(n) { case 0 : anchor=TEXT_ANCHOR_LEFT_TOP; text_x=1; text_y=1; break; case 1 : anchor=TEXT_ANCHOR_CENTER_TOP; text_x=form.Width()/2; text_y=1; break; case 2 : anchor=TEXT_ANCHOR_RIGHT_TOP; text_x=form.Width()-2; text_y=1; break; case 3 : anchor=TEXT_ANCHOR_LEFT_CENTER; text_x=1; text_y=form.Height()/2; break; case 4 : anchor=TEXT_ANCHOR_CENTER; text_x=form.Width()/2; text_y=form.Height()/2; break; case 5 : anchor=TEXT_ANCHOR_RIGHT_CENTER; text_x=form.Width()-2; text_y=form.Height()/2; break; case 6 : anchor=TEXT_ANCHOR_LEFT_BOTTOM; text_x=1; text_y=form.Height()-2; break; case 7 : anchor=TEXT_ANCHOR_CENTER_BOTTOM;text_x=form.Width()/2; text_y=form.Height()-2; break; case 8 : anchor=TEXT_ANCHOR_RIGHT_BOTTOM; text_x=form.Width()-2; text_y=form.Height()-2; break; default: anchor=TEXT_ANCHOR_CENTER; text_x=form.Width()/2; text_y=form.Height()/2; break; } form.TextOnBG(0,TextByLanguage("H-Градиент","H-Gradient"),text_x,text_y,anchor,C'211,233,149',255,true,true); //--- Increase the object click counter (and also the pointer to the text anchor point), //--- and if the value exceeds 8, reset the value to zero (from 0 to 8 = nine anchor points) n++; if(n>8) n=0; } } } }
单击第三个交互窗(ID 为 2)将在 FigureProcessing() 函数中处理。 点击第四个交互窗(ID 为 3),我们可以根据 n 来定义文本锚点角度,并显示文本。 但现在用文本动画框架对象类来显示文本。
根据按下的键返回造型种类的辅助函数:
//+------------------------------------------------------------------+ //| 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; //--- Default = Dot default : return FIGURE_TYPE_PIXEL; } } //+------------------------------------------------------------------+
处理交互窗对象点击的函数非常庞大。 不过,它却很简单,因为它用 switch 操作符来处理所绘制交互窗上对应的每个按钮。 处理很简单 — 设置初始坐标,检查它们是否超出可接受的数值范围,依据这些参数绘制图形,并增加参数值的偏移。 下次我们单击交互窗时,先前的造型会被恢复的背景所覆盖,并用其锚点的新坐标绘制新的造型。
//+------------------------------------------------------------------+ //| Handle the selected shape | //+------------------------------------------------------------------+ void FigureProcessing(CForm *form,const ENUM_FIGURE_TYPE figure_type) { int array_x[5]={0,0,0,0,0}; int array_y[5]={0,0,0,0,0}; switch(figure_type) { //--- "1" = Dot case FIGURE_TYPE_PIXEL : coordX1=START_X+nx1; coordY1=START_Y+ny1; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } form.SetPixelOnBG(0,coordX1,coordY1,clrWheat); nx1++; ny1++; break; //--- "2" = Dot with AntiAlliasing case FIGURE_TYPE_PIXEL_AA : coordX1=START_X+nx1; coordY1=START_Y+ny1; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } form.SetPixelAAOnBG(0,coordX1,coordY1,clrWheat); nx1++; ny1++; break; //--- "3" = Vertical line case FIGURE_TYPE_LINE_VERTICAL : coordX1=START_X+nx1; coordY1=START_Y; coordY2=form.Height()-START_Y-1; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } form.DrawLineVerticalOnBG(0,coordX1,coordY1,coordY2,clrWheat); nx1++; break; //--- "4" = Vertical segment of a freehand line having a specified width using a smoothing algorithm case FIGURE_TYPE_LINE_VERTICAL_THICK : coordX1=START_X+nx1; coordY1=START_Y; coordY2=form.Height()-START_Y-1; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } form.DrawLineThickVerticalOnBG(0,coordX1,coordY1,coordY2,5,clrWheat,255,true,false,STYLE_SOLID,LINE_END_SQUARE); nx1++; break; //--- "5" = Horizontal line case FIGURE_TYPE_LINE_HORIZONTAL : coordX1=START_X; coordX2=form.Width()-START_X-1; coordY1=START_Y+ny1; if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } form.DrawLineHorizontalOnBG(0,coordX1,coordX2,coordY1,clrWheat); ny1++; break; //--- "6" = Horizontal segment of a freehand line having a specified width using a smoothing algorithm case FIGURE_TYPE_LINE_HORIZONTAL_THICK : coordX1=START_X; coordX2=form.Width()-START_X-1; coordY1=START_Y+ny1; if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } form.DrawLineThickHorizontalOnBG(0,coordX1,coordX2,coordY1,5,clrWheat,255,true,false,STYLE_SOLID,LINE_END_ROUND); ny1++; break; //--- "7" = Freehand line case FIGURE_TYPE_LINE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawLineOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "8" = Line with AntiAlliasing case FIGURE_TYPE_LINE_AA : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawLineAAOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "9" = Line with WU case FIGURE_TYPE_LINE_WU : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawLineWuOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "0" = Segment of a freehand line having a specified width using a smoothing algorithm case FIGURE_TYPE_LINE_THICK : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawLineThickOnBG(0,coordX1,coordY1,coordX2,coordY2,3,clrWheat,255,true,false,STYLE_SOLID,LINE_END_SQUARE); nx1++; ny1++; nx2++; ny2++; break; //--- "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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolylineOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolylineAAOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolylineWuOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolylineSmoothOnBG(0,array_x,array_y,1,clrWheat,255,0.5,30.0,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolylineThickOnBG(0,array_x,array_y,3,clrWheat,255,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "y" = Polygon case FIGURE_TYPE_POLYGON : 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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolygonOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "u" = Filled polygon case FIGURE_TYPE_POLYGON_FILL : return; coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*4; coordY2=coordY1; coordX3=coordX2; coordY3=coordY2+ny3*2; coordX4=coordX1; coordY4=coordY3; //--- Fill in the arrays with coordinate values 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- Draw a shape form.DrawPolygonFillOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; break; //--- "i" = Polygon with AntiAlliasing case FIGURE_TYPE_POLYGON_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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolygonAAOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "o" = Polygon with WU case FIGURE_TYPE_POLYGON_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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolygonWuOnBG(0,array_x,array_y,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "p" = Polygon with a specified width using two smoothing algorithms case FIGURE_TYPE_POLYGON_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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolygonSmoothOnBG(0,array_x,array_y,3,clrWheat,255,0.5,10.0,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "a" = Polygon with a specified width using a smoothing algorithm case FIGURE_TYPE_POLYGON_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 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 if(array_x[0]>form.Width()-START_X-1) { nx1=0; array_x[0]=START_X; } if(array_y[0]>form.Height()-START_Y-1) { ny1=0; array_y[0]=START_Y; } //--- check x2 and y2 coordinates for being outside the form if(array_x[1]>form.Width()-START_X-1) { nx2=0; array_x[1]=START_X; } if(array_y[1]>form.Height()-START_Y-1) { ny2=0; array_y[1]=array_y[0]; } //--- check x3 and y3 coordinates for being outside the form if(array_x[2]>form.Width()-START_X-1) { nx3=0; array_x[2]=array_x[1]; } if(array_y[2]>form.Height()-START_Y-1) { ny3=0; array_y[2]=array_y[1]; } //--- check x4 and y4 coordinates for being outside the form if(array_x[3]>form.Width()-START_X-1) { nx4=0; array_x[3]=START_X; } if(array_y[3]>form.Height()-START_Y-1) { ny4=0; array_y[3]=array_y[2]; } //--- check x5 and y5 coordinates for being outside the form if(array_x[4]>form.Height()-START_X-1) { nx5=0; array_x[4]=array_x[0]; } if(array_y[4]>form.Height()-START_Y-1) { ny5=0; array_y[4]=array_y[0]; } //--- Draw a shape form.DrawPolygonThickOnBG(0,array_x,array_y,3,clrWheat,255,true,false,STYLE_SOLID,LINE_END_BUTT); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; nx4++; ny4++; nx5++; ny5++; break; //--- "s" = Rectangle case FIGURE_TYPE_RECTANGLE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawRectangleOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "d" = Filled rectangle case FIGURE_TYPE_RECTANGLE_FILL : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawRectangleFillOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "f" = Circle case FIGURE_TYPE_CIRCLE : coordX1=START_X+nx1; coordY1=START_Y+ny1; RD=nx2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(RD>form.Height()/2) { nx2=0; RD=1; } form.DrawCircleOnBG(0,coordX1,coordY1,(int)RD,clrWheat); nx1++; ny1++; nx2++; break; //--- "g" = Filled circle case FIGURE_TYPE_CIRCLE_FILL : coordX1=START_X+nx1; coordY1=START_Y+ny1; RD=nx2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(RD>form.Height()/2) { nx2=0; RD=1; } form.DrawCircleFillOnBG(0,coordX1,coordY1,(int)RD,clrWheat); nx1++; ny1++; nx2++; break; //--- "h" = Circle with AntiAlliasing case FIGURE_TYPE_CIRCLE_AA : coordX1=START_X+nx1; coordY1=START_Y+ny1; RD=nx2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(RD>form.Height()/2) { nx2=0; RD=1; } form.DrawCircleAAOnBG(0,coordX1,coordY1,RD,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; break; //--- "j" = Circle with WU case FIGURE_TYPE_CIRCLE_WU : coordX1=START_X+nx1; coordY1=START_Y+ny1; RD=nx2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(RD>form.Height()/2) { nx2=0; RD=1; } form.DrawCircleWuOnBG(0,coordX1,coordY1,RD,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; break; //--- "k" = Triangle case FIGURE_TYPE_TRIANGLE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*4; coordY2=START_Y+ny2*2; coordX3=coordX1+nx3*2; coordY3=coordY2+ny3*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } if(coordX3>form.Width()-START_X-1) { nx3=0; coordX3=START_X; } if(coordY3>form.Height()-START_Y-1) { ny3=0; coordY3=START_Y; } form.DrawTriangleOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; break; //--- "l" = Filled triangle case FIGURE_TYPE_TRIANGLE_FILL : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*4; coordY2=START_Y+ny2*2; coordX3=coordX1+nx3*2; coordY3=coordY2+ny3*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } if(coordX3>form.Width()-START_X-1) { nx3=0; coordX3=START_X; } if(coordY3>form.Height()-START_Y-1) { ny3=0; coordY3=START_Y; } form.DrawTriangleFillOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; break; //--- "z" = Triangle with AntiAlliasing case FIGURE_TYPE_TRIANGLE_AA : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*4; coordY2=START_Y+ny2*2; coordX3=coordX1+nx3*2; coordY3=coordY2+ny3*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } if(coordX3>form.Width()-START_X-1) { nx3=0; coordX3=START_X; } if(coordY3>form.Height()-START_Y-1) { ny3=0; coordY3=START_Y; } form.DrawTriangleAAOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; break; //--- "x" = Triangle with WU case FIGURE_TYPE_TRIANGLE_WU : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*4; coordY2=START_Y+ny2*2; coordX3=coordX1+nx3*2; coordY3=coordY2+ny3*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } if(coordX3>form.Width()-START_X-1) { nx3=0; coordX3=START_X; } if(coordY3>form.Height()-START_Y-1) { ny3=0; coordY3=START_Y; } form.DrawTriangleWuOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; ny2++; nx3++; ny3++; break; //--- "c" = Ellipse case FIGURE_TYPE_ELLIPSE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawEllipseOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "v" = Filled ellipse case FIGURE_TYPE_ELLIPSE_FILL : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=START_X+nx2*2; coordY2=START_Y+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawEllipseFillOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "b" = Ellipse with AntiAlliasing case FIGURE_TYPE_ELLIPSE_AA : return; coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*2; coordY2=coordY1+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawEllipseAAOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; ny2++; break; //--- "n" = Ellipse with WU case FIGURE_TYPE_ELLIPSE_WU : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=coordX1+nx2*2; coordY2=coordY1+ny2*2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawEllipseWuOnBG(0,coordX1,coordY1,coordX2,coordY2,clrWheat,255,true,false,STYLE_SOLID); nx1++; ny1++; nx2++; ny2++; break; //--- "m" = Ellipse arc case FIGURE_TYPE_ARC : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=form.Width()-START_X-1-nx2; coordY2=form.Height()-START_Y-1-ny2; coordX3=coordX1; coordY3=coordY1; coordX4=coordX2; coordY4=coordY2; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX3=coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY3=coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX4=coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY4=coordY2=START_Y; } form.DrawArcOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,coordX4,coordY4,clrWheat); nx1++; ny1++; nx2++; ny2++; break; //--- "," = Ellipse sector case FIGURE_TYPE_PIE : coordX1=START_X+nx1; coordY1=START_Y+ny1; coordX2=form.Width()-START_X-1-nx2; coordY2=form.Height()-START_Y-1-ny2; coordX3=coordX1; coordY3=coordY1; coordX4=coordX2; coordY4=coordY1; if(coordX1>form.Width()-START_X-1) { nx1=0; coordX3=coordX1=START_X; } if(coordY1>form.Height()-START_Y-1) { ny1=0; coordY3=coordY4=coordY1=START_Y; } if(coordX2>form.Width()-START_X-1) { nx2=0; coordX4=coordX2=START_X; } if(coordY2>form.Height()-START_Y-1) { ny2=0; coordY2=START_Y; } form.DrawPieOnBG(0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,coordX4,coordY4,clrWheat,clrLightSteelBlue); nx1++; ny1++; nx2++; ny2++; break; //--- Default = Nothing default : break; } } //+------------------------------------------------------------------+
我相信,函数代码简单易懂。 无论如何,欢迎您参与评论部分。
我尚未针对简单测试进行雷同参数的一般处理。 在 'switch' 运算符的情况下,显式地编写所有内容会更容易且更快捷。 代码重复性并不重要。 对我们来说,简单地检查所创建类的功能更为重要。
编译 EA,并在图表上启动它。 启动后,按几个键以确保绘图模式发生变化,并显示在第三个交互窗的标签当中。 单击第四个交互窗时,文本的移动方式与上一篇文章中的 EA 移动方式相同。 只是现在这个文本是通过动画文本框对象类来呈现的。
如果在选择了所需的绘图模式后,我们开始单击第三个交互窗(其上显示绘图模式描述的标签),则所选造型将绘制在标签上方,整个交互窗的造型锚点坐标每次都会改变化.
似乎并非所有绘图方法都能顺利运行。 例如,CCanvas 类的 FillPolygon() 方法在点击对象时,若以指定初始参数的情况下,只会陷入无限循环,而 EllipseAA() 方法则会产生零除法错误。 该方法具有可能发生这种情况的潜在点:
//+------------------------------------------------------------------+ //| Draw ellipse with antialiasing | //+------------------------------------------------------------------+ void CCanvas::EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX) { double rx = (x2-x1)/2; double ry = (y2-y1)/2; //--- preliminary calculations double x=(x2>x1) ? x1+rx : x2+rx; double y=(y2>y1) ? y1+ry : y2+ry; double rx2=rx*rx; double ry2=ry*ry; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<<m_style_idx; //--- draw double quarter=round(rx2/sqrt(rx2+ry2)); for(double dx=0; dx<=quarter; dx++) { double dy=ry*sqrt(1-dx*dx/rx2); if((m_style&mask)==mask) PixelSet4AA(x,y,dx,dy,clr); mask<<=1; if(mask==0x1000000) mask=1; } quarter=round(ry2/sqrt(rx2+ry2)); for(double dy=0; dy<=quarter; dy++) { double dx=rx*sqrt(1-dy*dy/ry2); if((m_style&mask)==mask) PixelSet4AA(x,y,dx,dy,clr); mask<<=1; if(mask==0x1000000) mask=1; } //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; } //+------------------------------------------------------------------+
我在个人资料线索中报告了这个问题。 在收到开发人员的回复之前,我会在后续文章中尝试绕过此类错误。
关于这一点,在鼠标点击交互窗对象的处理程序中,有些绘制方法不会调用。 代之,该函数只是简单地返回。 无论如何,这将在以后修复。 但现在我们看到所绘制造型之下的背景已被恢复,一切都按预期那样显示。 切换绘图模式有些问题 — 切换后,背景没有被恢复。 发生这种情况是由于缺乏恢复背景时去除图像的方法。 我将在后续文章中完成这一点。 至此,出于测试目的,我简单地切换了时间帧,从而在交互窗上显示另一个造型之前,完整恢复背景。
下一步是什么?
在下一篇文章中,我将继续开发动画类。
以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
请您在评论中留下问题和建议。
*该系列的前几篇文章:
DoEasy 函数库中的图形(第七十三部分):图形元素的交互窗对象
DoEasy 函数库中的图形(第七十四部分):由 CCanvas 类提供强力支持的基本图形元素
DoEasy 函数库中的图形(第七十五部分):处理基本图形元素图元和文本的方法
DoEasy 函数库中的图形(第七十六部分):会话窗对象和预定义的颜色主题
DoEasy 函数库中的图形(第七十七部分):阴影对象类
DoEasy 函数库中的图形(第七十八部分):函数库中的动画原理 图像切片