Graphics in DoEasy library (Part 79): "Animation frame" object class and its descendant objects
Contents
- Concept
- Improving library classes
- "Animation frame" object class
- Text animation frame class
- Rectangle animation frame class
- Form animation class
- Test
- What's next?
Concept
In the previous article, I have created the class for saving and subsequent restoring of a part of a background located under a drawn shape. Here I will resume working on the concept and create several classes based on it: the base class of a single animation frame and its descendants — the text animation frame and the rectangle animation frame classes.
The base class is to contain a common set of properties for a single animation frame, while its descendants are to have their own inherent methods for drawing shapes. The text animation class is to allow working with texts, while the rectangle animation frame is to allow creating a single animation frame and drawing various shapes in it using drawing methods based on such CCanvas class methods.
Each created form object is to have a set of methods for drawing on a custom canvas, which allows us to quickly create and manage new images on the form. To be able to conveniently use drawing tools in each form, I will create a common class to feature the lists of all created text and shape images on the form. Later, I will add new animation methods, and their lists will also be placed to the common class. Such a concept allows us to dynamically create new images and save them to the appropriate lists. Next, it will be possible to quickly retrieve them from the form object and display on its background. In this case, such objects will automatically save the background of the form under them. In case the objects are deleted, changed or moved, the saved background is restored.
Thus, in the current article, I will slightly revise drawing frames created in the previous articles, develop the class of the basic animation frame object and develop two classes of its descendants — the text animation frame and the rectangle animation frame classes. Let's create the class for storing the lists of these frame objects and provide the ability to work with them from the form object.
Improving library classes
First of all, let's improve the previously created library classes. In \MQL5\Include\DoEasy\Defines.mqh, add the list of animation frames and the list of drawn shape types in the rectangle animation frame class:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
I will use animation frame types to identify animation frame objects (be it a text, a drawn shape or any other animation frame type I will introduce in the following articles. Types of drawn shapes will indicate what exactly is drawn in a single rectangle animation frame. These types correspond to existing drawing methods in the CCanvas class ("Data access", "Draws primitives", "Draws filled primitives" and "Draws primitives with antialiasing" sections in the table of class methods).
In \MQL5\Include\DoEasy\Data.mqh, add new message indices:
//--- 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
and message texts corresponding to newly added indices:
//--- 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"},
In the file of the library service functions \MQL5\Include\DoEasy\Services\DELib.mqh, add the functions returning the maximum and minimum values in the array:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The functions return the maximum or minimum value located in the array passed to them via a link. They are of bool type since any returned value from the function may be located in the array cells as an "invalid" one. For example, if -1 is returned upon receiving data from the array (like it is done in many functions), such a value can be one of those set in the array. When returning a valid value (-1), our program assumes that this is an error. This is incorrect. Therefore, we will return false in case of an error, while the found maximum or minimum value in the array will be set to the variable passed to the function via a link. If the function returns true, the variable stores the desired value. The source variable receives the name of the method the function was called from. In case of an error, this allows us to see the name of the method the function has been called from, as well as the error message.
The variable also receives the function returning the description of the drawn shape type:
//+------------------------------------------------------------------+ //| Return the description of the drawn shape type | //+------------------------------------------------------------------+ string FigureTypeDescription(const ENUM_FIGURE_TYPE figure_type) { return(StringSubstr(EnumToString(figure_type),12)); } //+------------------------------------------------------------------+
Here, the type passed to the function in the enumeration format is converted into the string description. The substring from the position of the 12th symbol is retrieved from the type text representation for cropping an unnecessary text. For example, FIGURE_TYPE_TRIANGLE figure type is converted into "FIGURE_TYPE_TRIANGLE", and the necessary substring starting from the 12th symbol "FIGURE_TYPE_TRIANGLE" is retrieved from this text. The "TRIANGLE" string is returned as a result.
In the previous article, when creating the methods for copying a part of the background to the array, I defined the size and coordinates of the copied background rectangle by a text size displayed on the form. Here I will display images as well. Their size will no longer be defined by a text size. Therefore, we need to create a method defining the coordinates and the size of the copied rectangle of the background part.
In the \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh file of the graphical element class, rename the method
//--- 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
Now the method is called GetShiftXYbyText(). Let's declare a new method returning the coordinates and the size of the copied part of the image by the specified size relative to the object anchor point:
//--- 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
Implement them in the end of the class listing.
The method returning coordinate offsets relative to the rectangle anchor point by size:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Here, depending on the width and height of the copied area, as well as on the anchor point passed to the method, calculate coordinate offsets relative to the anchor point and write them to the variables passed to the method via a link.
The method returning coordinate offsets relative to the text anchor point:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Here we first define the size of the text passed to the method and call the method considered above for defining the coordinate offset of the saved form background image area.
In the previous article, I developed the class for copying a part of the form background image and its subsequent restoration from the array. The class was set in the form object class file. Now I am going to remove the class from the form object class file relocating it to the new file of a new created frame object class (once again I find out that it is better to write and store classes in separate files).
So, let's create a base class for a single animation frame. The class is to contain the properties that are common for all of its descendants.
"Animation frame" object class
In \MQL5\Include\DoEasy\Objects\Graph\, create the new Animations\ folder with the new Frame.mqh file of the CFrame class.
The file of the graphical element object class should be included to the class file:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Next, place the pixel copier object class removed from the form object class file (considered in the previous article):
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The only thing that has been (temporarily) changed here is the fact that I have commented out the code strings that should copy the entire form image saved previously. The error has been made here causing the background to be copied directly from the graphical resource rather than from the form object array. The graphical resource contains all the changes applied to the form background image. To fix this, I will need to save the form appearance to a separate array featuring the copy of the original form image. I already have such an array but I still need to create the methods for saving the original form appearance right after its creation. Till then, I have commented out these strings. The background with the size of the entire form (rather than of its part) will be restored in the loop of restoring the part of the form background (i.e. not by copying one array to another but rather by filling the form background element by element from the array storing the saved copy of the part of the form background image).
Next, after the pixel copier class listing, write the body of the animation frame object class:
//+------------------------------------------------------------------+ //| 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); }; //+------------------------------------------------------------------+
The class is derived from the pixel copier object class, so, in fact, it IS the pixel copier object class.
All variables and methods declared in the class are described in the comments. Since the class is basic for other animation frame classes, all properties and methods common to the descendants are set here.
The variables and methods returning the last coordinates, offsets and anchor points are necessary so that we are able to define the coordinates of the previous saved part of the image when restoring it. These coordinates are to be used for placing the saved background that has been erased by the image drawn over it.
The class features three constructors:
- default public constructor,
- protected text frame object constructor,
- protected rectangle frame object constructor.
Let's consider the implementation of protected constructors.
Constructor of rectangle frames:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The constructor receives the ID of the created rectangle frame object, its X and Y coordinates, frame width and height, as well as the pointer to the graphical element object the new object is created of. Since the class is a descendant of the pixel copier object, pass all the necessary parameters to the base class constructor in the constructor initialization list. These parameters are all the properties passed to the constructor arguments.
Set the default parameters to all class member variables in the class body.
The constructor of text frames:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
The constructor receives the ID of the created text frame object, its X and Y coordinates, the text and the pointer to the graphical element object the new object is created of.
In the class body, first define the text size, then set the default values of class member variables and set the ID of the created object, its coordinates and text size to the parent pixel copier object.
Create the classes of objects of animation frame object class descendants.
Text animation frame class
In \MQL5\Include\DoEasy\Objects\Graph\Animations\, create the new file FrameText.mqh of the CFrameText class.
The file of the animation frame class should be included into the file, while the class itself should be its descendant:
//+------------------------------------------------------------------+ //| 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) {} }; //+------------------------------------------------------------------+
Here we can see one public method for drawing the text on the background of the form object and two constructors — default and parametric ones.
The parametric constructor receives the ID of the created text animation frame object and the pointer to the graphical element the object is created of. In the initialization list, the parent class receives the ID passed in the constructor arguments, default values for coordinates and a text, as well as the pointer to the graphical element also passed in the constructor arguments.
The method displaying a text on the background, while saving and restoring the background:
//+-------------------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The method logic is described in detail in the code comments. I have already considered it in the previous article during the test (a similar logic was set in the OnChartEvent() handler of the test EA), so, I believe, all is clear here. After the text has been drawn on the form, its anchor points, X and Y coordinates, as well as offset values relative to the anchor point are set in the variables of the parent class. Their values will be used to restore the form image background overwritten by the text.
Now let's create the second descendant class of the animation frame object.
Rectangle animation frame class
In \MQL5\Include\DoEasy\Objects\Graph\Animations\, create the new file FrameQuad.mqh of the CFrameQuad class.
The parent class file should be included into (and derived from) the class file:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
In the private section of the class, declare the class member variables storing coordinates and size of the rectangle enclosing the drawn shape — these are the coordinates and size of the saved background part image area overwritten by the drawn shape.
The parametric constructor receives the ID of the created object and the pointer to the graphical element the object is created of. The ID passed in the method arguments, default coordinate and frame size parameters and the pointer to the graphical element are passed to the parent class constructor in the constructor initialization list. In the constructor body, set the anchor point of the drawn figure as "top left". This is necessary to calculate the offset of the copied area. With this value, the anchor points of X and Y coordinate offsets are equal to zero.
Since we have plenty of drawing methods in the CCanvas class, all appropriate methods for drawing shapes on the background of the form object are declared in the public section of the class followed by restoring the background:
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 }; //+------------------------------------------------------------------+
Implementation of each of these methods is similar to the implementation of similar drawing methods. However, almost all of them have their nuances inherent in its drawing method (the size of the saved area of two similar drawing methods may be different due to individual features of each of them).
Let's have a look at the implementation of these methods.
The method setting the color of the point with specified coordinates:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The logic of the method is commented in sufficient detail in the code. Let's consider this more thoroughly. Here we first set X and Y coordinates of the upper left edge of the background rectangle area to be saved in the array for subsequent background restoration under the drawn point. Since this is only a dot (one pixel image), the coordinates of the saved area match the coordinates of the drawn dot and the size matches the size of a single pixel, i.e. 1 x 1.
Next we check whether the background has been previously saved (by the non-zero array size the background is saved into). If yes, restore the previously saved form object background (the coordinates and the size of the restored area are already set in the class variables). After the background is successfully restored, save the form background with the dot new coordinates and draw the dot. Save the new coordinates, as well as the size of the saved area and the shift, in the class variables for subsequent restoration of the background erased by the newly drawn dot.
The method drawing a segment of a vertical line:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the calculation of the coordinates and the size of the rectangle outlining the shape are different from the calculation in the previous method. This is natural since we draw a vertical line with the width of one pixel. The height of the line should be calculated as a difference between the maximum and minimum values of two Y coordinates of the line. The Y coordinate of the saved area should correspond to the minimum value of the two Y coordinates (the upper point of the drawn line).
The method drawing a segment of a horizontal line:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the calculation of the coordinates and the size of the saved area are similar to the same calculation in the previous method, except that this is a horizontal line and the height here is equal to one pixel, while the width and the X coordinate of the saved area should be calculated.
The method drawing a segment of a freehand line:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the coordinates and the size of the saved area are calculated based on the coordinates of the drawn line.
The method drawing a polyline:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the idea behind the calculation of the coordinates and the size of the saved area is the same as the one behind the methods considered above. However, the execution is different since in case of a polyline (as well as many other shapes), the coordinates are passed in arrays rather than in the form of variables as it is impossible to know the number of line bends or the number of coordinates to be passed in the method arguments beforehand. This is why before calling the method, we should fill in two arrays with X coordinates and the appropriate Y coordinates of each line bend dot.
In the method, we receive the maximum and minimum values from the arrays using the previously considered function returning the minimum or maximum value from the array passed to the function. The obtained values are used for calculating the coordinates and the size of the saved form background area.
The remaining shape drawing methods without smoothing (just pay attention to the calculation of the coordinates and the size of the saved area):
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Let's consider the methods of drawing filled primitives without smoothing.
The method filling the area:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Since the method fills in an arbitrary enclosed area, we cannot know the size of the saved area in advance. Therefore, the entire form should be saved here. To achieve this, let's set the coordinates and the size to zero. In case of these values, the method, which saves the rectangular area of the image, saves the entire form background to the array.
As for the remaining methods of drawing filled primitives, their calculation of coordinates and saved area size matches the calculation of simple non-smoothed primitives considered earlier. Let's have a look at the methods as they are:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The methods of drawing primitives with smoothing.
The method drawing a point using AntiAliasing algorithm:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here, the calculation of saved area coordinates and size are different from the same calculation in the point drawing method without smoothing. A smoothed point can be located on three adjacent pixels (9 in total, that is 3 x 3 pixels), therefore the dimensions of the saved area should be three pixels high and three pixels wide. The X and Y coordinates, respectively, should be one pixel to the left and one pixel above the coordinates of the point itself. Thus, the saved area rectangle outlining the point will have a margin of one pixel on all sides of the drawn point in case the latter is blurred by the smoothing algorithm and drawn on more than one pixel. This will allow us to get rid of the incomplete restoration of the background erased by a drawn point with smoothing.
The method drawing a segment of a freehand line using AntiAliasing algorithm:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Testing the method revealed that the edges of the drawn line are not blurry, therefore the calculation of the saved area size matches the calculation in the line drawing method without smoothing.
The method drawing a segment of a freehand line using Wu algorithm:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the calculation is similar to the previous method for the same reason.
The method drawing a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
In this method, calculation of a saved area is different from the ones considered above. Since a line with such a smoothing algorithm features an editable value and an appearance of its ends, the width of the saved area should consider the size (width) of the line and its edges (the line edges can be rounded, therefore the line size (length) increases by two rounding radii, i.e. by the value set as a line width).
The method drawing a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration:
//+---------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here the calculation is the same as described for the previous method.
The remaining methods of drawing smoothed and other primitives:
//+-----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Here, the saved background area calculation algorithms of all methods are approximately identical to the calculation algorithms in the methods considered above.
In the ellipse drawing methods (DrawEllipseAAOnBG and DrawEllipseWuOnBG), if the rectangle the ellipse is drawn within is less than three pixels, the shape is not drawn. Therefore, the calculations here contain the check for the size less than three pixels. I have not decided yet, whether this is my bug or a feature of the CCanvas class methods. I hope to clarify this later.
I have developed all classes of the animation frame objects that I currently need.
Now it is time to create a class to store, access and manage created animation frame objects.
The class is to contain two (for now) lists for storing created animation frame objects (text and rectangle ones), as well as to have the methods of creating and managing new objects. Subsequently, the class is to store all animation objects belonging to one form. Thus, each form is to feature its own set of animation objects that can be dynamically created and added to the list of form animations.
Form animation class
In \MQL5\Include\DoEasy\Objects\Graph\Animations\, create a new Animations.mqh file of the CAnimations class.
Only newly created descendant class files of the base animation frame object should be included into the class file, while the class itself should be a descendant of the base object of the CObject standard library:
//+------------------------------------------------------------------+ //| 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 { }
In the private section of the class, declare the pointer to the graphical element object animation objects are to be created from, two lists for storing two types of animation frame objects and the methods for returning the flag indicating the presence of a specified object in the list, as well as the methods returning the pointer to the existing animation frame object or preliminarily creating it if it is not in the list:
//+------------------------------------------------------------------+ //| 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:
All methods are considered below.
In the public section of the class, declare the methods for creating and working with objects in the lists and the methods of drawing primitives, while saving and restoring the background:
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 }; //+------------------------------------------------------------------+
Let's consider the implementation of the declared methods.
The parametric constructor:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CAnimations::CAnimations(CGCnvElement *element) { this.m_element=element; } //+------------------------------------------------------------------+
The value of the pointer to the graphical element object passed in the arguments is set for m_element pointer.
The method returning the pointer to the animation frame object by type and 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; } //+------------------------------------------------------------------+
The method logic is described in details in the code comments and requires no explanations.
The method returning the flag indicating the presence of the frame object with the specified type and ID in the list:
//+-----------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
The method returns the bool result of calling the GetFrame() method considered above. If the GetFrame() method returns a non-NULL result (the required object is present in the list), the method returns true, otherwise — false.
The method creating a new text animation frame object:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The method logic is fully described in the code comments.
The method creating a new rectangle animation frame object:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
The method is identical to the one considered above.
The method returning the pointer or creating a new animation frame object:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
The method logic is described in the code comments. If we need to work with an animation frame, we can create it beforehand, get the pointer to it and manage the obtained object. If we need to dynamically create objects, this method allows us to first create a new object (provided that an object with a specified ID is absent) and return the pointer to it. Thus, it is possible to arrange dynamic creation of an object as appropriate, get the pointer to it right away and manage it.
The methods of working with animation frame objects.
The method displaying a text on the background, while saving and restoring the background:
//+--------------------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
The method receives the object ID, displayed text parameters (the text itself, X and Y coordinates, anchor point, color and opacity), the flag indicating the need to create a new object with the specified ID, in case the object with such an ID is not present in the list, and the chart redraw flag.
Next, get the pointer to the required object (or create the object if it is absent). If failed to get the pointer, return false.
If the pointer is received, return the result of the TextOnBG() method of the obtained text animation frame object.
The method setting the color of the point with specified coordinates:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
The method logic is identical to the one of the method considered above. The method receives the object ID, drawn shape X and Y coordinates, its color and opacity, the flag indicating the need to create a new object with the specified ID, in case the object with such an ID is not present in the list, and the chart redraw flag.
Next, get the pointer to the required object (or create the object if it is absent). If failed to get the pointer, return false.
If the pointer is received, return the result of the SetPixelOnBG() method of the obtained animation rectangle frame object.
Other methods of drawing primitives.
The logic of the remaining shape drawing methods is identical to the logic of the methods considered above. Let's have a look at their listing:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
The newly created class of animation objects should be an integral part of the form object. Thus, each form is to have its own methods of creating images.
Open the file of the form object class \MQL5\Include\DoEasy\Objects\Graph\Form.mqh and add the necessary improvements.
Include the file of the animation object class:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Remove the pixel copier object class from the listing (I have moved it to another file):
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GCnvElement.mqh" #include "ShadowObj.mqh" #include "Animations\Animations.mqh" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CPixelCopier : public CObject { private: ... } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+
In the private section of the class, instead of the pixel copier list
CArrayObj m_list_pc_obj; // List of pixel copier objects
declare the pointer to the animation class object:
//+------------------------------------------------------------------+ //| 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:
Remove the declaration of the already unnecessary IsPresentPC() method from the private section, as well as its implementation from the code listing:
//--- 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:
Remove the already unnecessary method from the public section of the class:
//--- 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; }
and add the new methods returning the pointers to the animation object and the lists of text and rectangle animation frames:
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
Remove the declaration of the method for creating a new pixel copier object:
//--- 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
The implementation of the method written outside the class body is removed as well.
In the block of methods for working with image pixels in the public section of the class, write the new methods for creating animation frame objects, returning the pointers to created objects and drawing methods that save and restore the background:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
All methods return the results of the appropriate methods from the CAnimations animation object created above.
In the class destructor, remove the animation object:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CForm::~CForm() { if(this.m_shadow_obj!=NULL) delete this.m_shadow_obj; if(this.m_animations!=NULL) delete this.m_animations; } //+------------------------------------------------------------------+
In the initialization method, create a new animation object:
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+
Now a new animation object is created automatically when creating a new form object. We can dynamically add new animation frames to the animation object or create the predefined forms. Upon the completion of the program run, the animation object is destroyed to avoid memory leaks.
We are ready to test the created classes.
We have a test EA, in which four forms are drawn. When we click the fourth (bottom) form, the H-Gradient label moves (H - for horizontal filling type). This label will now be created and moved as a text animation frame object. The third form (the vertical filling) is to be used for displaying various drawn shapes. Before drawing a shape, we first display a form text as a text animation frame. Shapes are drawn above it. Switching the types of drawn figures is done by pressing the keys, while the text displays the selected drawn figure. Each mouse click on the shape changes the coordinates of the drawn shape points.
Test
To perform the test, let's use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part79\ as TestDoEasyPart79.mq5.
In the area of global variables, enter macro substitutions for specifying initial coordinates of drawn shapes and declare the variables for storing coordinates of different points of drawn shapes (five points are to be used in total) and their change values:
//+------------------------------------------------------------------+ //| 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; //+------------------------------------------------------------------+
In the blocks of creating two last forms of the OnInit() handler, the text is created using text animation objects:
//--- 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
I have created these text objects here beforehand. Drawn shapes are to be created dynamically — upon clicking in the form area.
To handle keystrokes, add the following code block to the OnChartEvent() handler:
//--- 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
Slightly change the code in the click handling block by forms — now the text will be displayed using text animation objects of the form being clicked:
//--- 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; } } } }
Clicking the third form (the ID of 2) is to be handled in the FigureProcessing() function. Clicking the fourth form (the ID of 3) allows us to define the text anchor angle depending on n and display the text. But the text is now displayed using the text animation frame object class.
The auxiliary function returning the shape type depending on the pressed key:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
The function handling the click on the form object is quite bulky. However, it is simple as it features the switch operator handling each pressed button the drawn form corresponds to. The handling is simple — set the initial coordinates, check them for being out of the acceptable value range, draw a figure with these parameters and increase the shifts of the parameter values. The next time we click the form, the previous shape is overwritten by the restored background and a new one is drawn using the new coordinates of its points.
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
I believe, the function code is simple and easy to understand. In any case, you are welcome to use the comments section.
I have not performed a general handling of identical parameters for a simple test. It is much easier and faster to write everything explicitly in the 'switch' operator cases. The code repetitiveness is not important. It is more important for us to simply check the functionality of the created classes.
Compile the EA and launch it on the chart. After the launch, press a few keys to make sure the drawing mode changes and is displayed in the label on the third form. When clicking the fourth form, the text is moved the same way as in the past EA from the previous article. Only now this text is rendered through the animation text frame object class.
If, after selecting the required drawing mode, we start clicking the third form (the label with a description of the drawing mode is displayed on), the selected shape is drawn above the label and the entire form with the shape points coordinates changing each time.
Not all drawing methods seem to work smoothly. For example, the FillPolygon() method of the CCanvas class simply falls into an infinite loop in case of the specified initial parameters in the object click, while the EllipseAA() method generates a zero divide error. This method features potential points where this may happen:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
I reported this issue in the profile thread. Till I have a response from the developers, I will try to bypass such errors in subsequent articles.
Regarding this, some drawing methods are not called in the handler of mouse clicks on the form object. Instead, the function simply makes a return. Anyway, this will be fixed later. But now we see that the background under the drawn shapes is restored and everything is displayed as intended. There is a problem with switching drawing modes — after switching, the background is not restored. This happens due to the absence of the method for removing an image with the background restoration. I will do this in subsequent articles. Here, for testing purposes, I have simply switched the timeframe to completely restore the background before displaying another shape on the form.
What's next?
In the next article, I will continue the development of the animation classes.
All files of the current version of the library are attached below together with the test EA file for MQL5 for you to test and download.
Leave your questions and suggestions in the comments.
*Previous articles within the series:
Graphics in DoEasy library (Part 73): Form object of a graphical element
Graphics in DoEasy library (Part 74): Basic graphical element powered by the CCanvas class
Graphics in DoEasy library (Part 75): Methods of handling primitives and text in the basic graphical element
Graphics in DoEasy library (Part 76): Form object and predefined color themes
Graphics in DoEasy library (Part 77): Shadow object class
Graphics in DoEasy library (Part 78): Animation principles in the library. Image slicing
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/9652
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use