English Русский 中文 Deutsch 日本語 Português
Gráficos en la biblioteca DoEasy (Parte 79): Clase de objeto "Fotograma de animación" y sus objetos herederos

Gráficos en la biblioteca DoEasy (Parte 79): Clase de objeto "Fotograma de animación" y sus objetos herederos

MetaTrader 5Ejemplos | 6 septiembre 2021, 12:52
457 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto

En el último artículo, creamos una clase para guardar y después restaurar la parte del fondo que está debajo de la figura dibujada. Hoy continuaremos con este concepto y crearemos varias clases basadas en ella, es decir, la clase básica de un fotograma de animación y sus descendientes: la clase de fotograma de animación de texto y la clase de fotograma de animación rectangular.

La clase básica contendrá el conjunto general de propiedades de un fotograma de animación, mientras que sus descendientes contendrán sus propios métodos (inherentes solo a ellos) para dibujar figuras. La clase de animación de texto nos permitirá trabajar con textos, mientras que la clase de fotograma de animación rectangular nos permitirá crear un fotograma de animación y dibujar varias figuras en él usando los métodos de dibujo basados ​​en dichos métodos de la clase CCanvas.

Cada objeto de formulario creado tendrá un conjunto de métodos para dibujar en su lienzo, lo cual nos permitirá crear y gestionar nuevas imágenes en el formulario con gran rapidez. Para que podamos usar adecuadamente el kit de herramientas de dibujado en cada formulario, vamos a crear una clase general que contendrá las listas con todos los dibujos de texto y las figuras que se han creado en este formulario (más adelante, añadiremos nuevos métodos de animación, y sus listas también se encontrarán en esta clase general). Este concepto nos permitirá crear y guardar de forma dinámica las nuevas imágenes en las listas correspondientes a su tipo; luego podremos encontrar estas rápidamente desde el objeto de formulario y mostrarlas en su fondo. En este caso, dichos objetos guardarán automáticamente el fondo del formulario debajo de ellos, y cuando se eliminen, cambien o muevan, se restaurará el fondo guardado.

Por consiguiente, hoy revisaremos ligeramente las clases de dibujado creadas en los artículos anteriores; asimismo, desarrollaremos una clase para el objeto de fotograma de animación básico y crearemos las dos clases de sus descendientes: la clase de fotograma de animación de texto y la clase de fotograma de animación rectangular. Vamos a crear una clase para almacenar las listas de estos objetos de fotograma y a ofrecer la posibilidad de trabajar con ellos desde el objeto de formulario.


Mejorando las clases de la biblioteca

Para comenzar, mejoraremos las clases de la bibloteca que hemos creado anteriormente. En el archivo \MQL5\Include\DoEasy\Defines.mqh, introducimos una lista con los tipos de fotograma de animación y una lista con los tipos de figuras dibujadas en la clase de fotograma de animación rectangular:

//+------------------------------------------------------------------+
//| 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
   
  };
//+------------------------------------------------------------------+

Los tipos de fotograma de animación se usaran para identificar los objetos de fotograma de animación (ya se trate de un texto, de una figura dibujada o de algún otro tipo de fotograma de animación, lo cual crearemos en artículos posteriores). Los tipos de figuras dibujadas nos dirán qué se está dibujando exactamente en un fotograma de animación rectangular. Estos tipos se corresponden con los métodos de dibujado disponibles en la clase CCanvas (secciones "Acceso a los datos", "Primitivas de dibujado", "Dibujado de primitivas coloreadas "y" Dibujado de primitivas usando suavizado" en el recuadro de métodos de las clase).

En el archivo \MQL5\Include\DoEasy\Data.mqh, escribimos los índices de los nuevos mensajes:

//--- 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

y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:

//--- 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"},


En el archivo de funciones de servicio de la biblioteca \MQL5\Include\DoEasy\Services\DELib.mqh, añadimos las funciones que retornan el valor maximo y mínimo en la matriz:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Las funciones retornan el valor máximo o mínimo que se encuentra en la matriz transmitida por enlace, y tienen el tipo bool, porque cualquier valor de retorno de la función puede ser "erróneo" en las celdas de la matriz. Por ejemplo, si al ocurrir el error de obtención de datos de la matriz, retornamos -1 (como se hace en muchas funciones), ese valor podrá ser uno de los escritos en la matriz. Al retornar un valor encontrado correctamente (-1), nuestro programa asumirá que se trata de un error. Y esto no es cierto. Por consiguiente, en caso de error, retornaremos false, y el valor máximo o mínimo encontrado en la matriz se escribirá en la variable transmitida por enlace a la función. Si la función retorna true, el valor necesario se almacenará en esta variable. En la variable source, transmitimos a la función el nombre del método desde el que se ha llamado esta, lo cual permitirá mostrar el nombre del método desde el que se ha llamado esta función y el texto del mensaje sobre el error.

También añadiremos una función que retorna la descripción del tipo de figura que se ha dibujado allí:

//+------------------------------------------------------------------+
//| Return the description of the drawn shape type                   |
//+------------------------------------------------------------------+
string FigureTypeDescription(const ENUM_FIGURE_TYPE figure_type)
  {
   return(StringSubstr(EnumToString(figure_type),12));
  }
//+------------------------------------------------------------------+

Aquí, el tipo transmitido a la función en el formato de enumeración se convierte en una descripción de línea; la sublínea de la posición del duodécimo carácter se extrae de la representación textual del tipo para cortar el texto innecesario. Así, por ejemplo, el tipo de figura FIGURE_TYPE_TRIANGLE se convertirá al texto "FIGURE_TYPE_TRIANGLE" y la sublínea deseada partiendo del 12º carácter "FIGURE_TYPE_TRIANGLE" se extraerá de este texto. Como resultado, se retornará la línea "TRIANGLE".

En el último artículo, al crear los métodos para copiar una parte del fondo en una matriz, determinamos el tamaño y las coordenadas del rectángulo del fondo copiado según el tamaño del texto que se mostraba en el formulario. En esta ocasión, mostraremos no solo los textos, sino también las imágenes, y sus tamaños ya no estarán determinados por el tamaño del texto. Por consiguiente, necesitaremos crear un método que determine las coordenadas y el tamaño del rectángulo copiado de la parte del fondo.

En el archivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh de la clase de elemento gráfico, modificamos el nombre del método

//--- 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

Ahora, este método se llamará GetShiftXYbyText() y declarará un nuevo método que retorna las coordenadas y el tamaño de la parte copiada de la imagen según el tamaño especificado respecto al punto de anclaje del objeto:

//--- 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

Al final del listado de la clase, escribimos su implementación.

Método que retorna el desplazamiento de las coordenadas respecto al punto de anclaje del rectángulo según el tamaño:

//+------------------------------------------------------------------+
//| 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;
     }
  }
//+------------------------------------------------------------------+

Aquí, dependiendo de la anchura y la altura del área copiada, así como del punto de anclaje transmitido al método, calculamos el desplazamiento de las coordenadas respecto al punto de anclaje y las escribimos en las variables transmitidas por enlace al método.

Método que retorna el desplazamiento de las coordenadas respecto al punto de anclaje del texto:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

Aquí, primero determinamos el tamaño del texto transmitido al método, y luego llamamos al método anterior para determinar el desplazamiento de las coordenadas del área guardada de la imagen de fondo del formulario.

En el último artículo, desarrollamos una clase para copiar una parte de la imagen de fondo del formulario y luego restaurarla desde una matriz. Vamos a colocar esta clase en el archivo de clase del objeto de formulario. Hoy, eliminaremos esta clase del archivo de clase del objeto de formulario y la trasladaremos al nuevo archivo de la clase recién creada del objeto de fotograma (una vez más, consideramos que lo mejor es escribir y almacenar las clases en archivos separados).

Bien, vamos a crear la clase básica para un fotograma de animación. La clase contendrá las propiedades comunes a todos sus descendientes.

Clase de objeto "Fotograma de animación"

En la carpeta \MQL5\Include\DoEasy\Objects\Graph\, creamos la nueva carpeta Animations\, y en ella, el nuevo archivo Frame.mqh de la clase CFrame.

El archivo de clase debe incluir el archivo de clase del objeto de elemento gráfico:

//+------------------------------------------------------------------+
//|                                                        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"
//+------------------------------------------------------------------+

A continuación, colocaremos la clase de objeto de copiador de píxeles (la analizamos en el artículo anterior) eliminada del archivo de clase del objeto de formulario:

//+------------------------------------------------------------------+
//|                                                        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;
  }
//+------------------------------------------------------------------+

El único cambio (temporal) aquí son las líneas de código comentadas que deberían copiar toda la imagen del formulario guardada previamente. Aquí cometimos un error: el fondo no se ha copiado de la matriz del objeto de formulario, sino directamente del recurso gráfico, donde se encuentran solo los cambios realizados en la imagen del fondo del formulario. Para corregir esto, después de crear la ventana, necesitaremos guardarla en una matriz especialmente creada para dicho propósito, que almacenará una copia de la imagen original del formulario. Tenemos una matriz de este tipo, pero aún no hemos creado los métodos necesarios para mantener la apariencia original del formulario inmediatamente después de su creación. Para conseguirlo, deberemos pensar y escribir dichos métodos. Por consiguiente, si bien no disponemos de dichos métodos, vamos a comentar estas líneas. La restauración del fondo que contiene las dimensiones del formulario al completo (y no de parte del mismo), se realizará en el ciclo de restauración de la parte del fondo del formulario (es decir, no copiando una matriz en otra, sino rellenando los elementos del fondo del formulario desde la matriz en la que se almacena la copia guardada de la parte de la imagen del fondo del formulario).

A continuación, después del listado de la clase de copiador de píxeles, escribimos el cuerpo de la clase del objeto de fotograma de animación:

//+------------------------------------------------------------------+
//| 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);
  };
//+------------------------------------------------------------------+

La clase se hereda de la clase del objeto de copiador de píxeles, es decir, de hecho, es ella.

Todas las variables y métodos declarados en la clase se describen en los comentarios. Como esta clase constituirá la base para otras clases de fotograma de animación, aquí solo escribiremos todas las propiedades y métodos comunes a los herederos.

Necesitamos las variables y métodos que retornan las últimas coordenadas, los desplazamientos y los puntos de anclaje para que al restaurar una parte previamente guardada de la imagen del fondo del formulario, podamos saber en qué coordenadas se ha ubicado la última parte guardada de la imagen, de forma que podamos colocar en las mismas coordenadas y con el mismo desplazamiento respecto a los puntos de anclaje el fondo guardado que ha sido sobrescrito por la imagen dibujada sobre él.

La clase tiene tres constructores:

  1. el constructor público por defecto,
  2. el constructor protegido del objeto de fotograma de texto,
  3. el constructor protegido del objeto de fotograma rectangular.

Vamos a analizar la implementación de los constructores protegidos.

Constructor de fotogramas rectangulares:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Al constructor se transmite lo siguiente: el identificador del objeto de fotograma rectangular creado y sus coordenadas X e Y, así como la anchura y altura del fotograma. Además, se le transmite el puntero al objeto de elemento gráfico desde el cual se crea este nuevo objeto. Como la clase es heredera del objeto de copiador de píxeles, en la lista de inicialización del constructor, transmitimos todos los parámetros necesarios al constructor de la clase básica: precisamente estas serán todas las propiedades transmitidas en los argumentos del constructor.
En el cuerpo de la clase, establecemos los parámetros predeterminados para todas las variables de miembro de la clase.

Constructor de fotogramas de texto:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

Al constructor se transmite lo siguiente: el identificador del objeto de fotograma de texto creado y sus coordenadas X e Y, así como el texto y el puntero al objeto de elemento gráfico desde el cual se crea este nuevo objeto.
En el cuerpo de la clase, primero determinamos el tamaño del texto, después establecemos los valores predeterminados para las variables de miembro de clase y establecemos en el objeto padre de copiador de píxeles el identificador del objeto a crear, sus coordenadas y el tamaño del texto.

Vamos a crear las clases de los objetos herederos de la clase del objeto de fotograma de animación.

Clase del fotograma de animación de texto

En la carpeta \MQL5\Include\DoEasy\Objects\Graph\Animations\, creamos un nuevo archivo FrameText.mqh de la clase CFrameText.

El archivo debe incluir el archivo de clase de fotograma de animación, mientras que la propia clase debe ser su heredera:

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

Aquí vemos un método público para dibujar el texto en el fondo del objeto de formulario y dos constructores: el constructor predeterminado y el paramétrico.

En el constructor paramétrico, transmitimos el identificador del objeto de fotograma de animación de texto creado y el puntero al objeto de elemento gráfico a partir del cual se ha creado este objeto. En la lista de inicialización, transmitimos a la clase padre el identificador transmitido en los argumentos del constructor y los valores predeterminados para las coordenadas y el texto, así como el puntero al elemento gráfico también transmitido en los argumentos del constructor.

Método que muestra el texto en el fondo, con almacenamiento y restauración del fondo

//+--------------------------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

La lógica del método se describe con detalle en los comentarios al código; como ya la hemos analizado en el último artículo, durante la prueba (escribimos una lógica similar en el manejador OnChartEvent() del asesor de prueba), esperamos que el lector no tenga preguntas al respecto. Una vez se haya dibujado el texto en el formulario, escribiremos en las variables de la clase principal el punto de anclaje del mismo y las coordenadas X e Y, además de las dimensiones de desplazamiento relativas al punto de anclaje; sus valores se utilizarán para restaurar el fondo de la imagen del formulario sobrescrito por el texto.

Ahora, vamos a crear una segunda clase que heredará del objeto de fotograma de animación.

Clase del fotograma de animación rectangular

En la carpeta \MQL5\Include\DoEasy\Objects\Graph\Animations\, creamos un nuevo archivo FrameQuad.mqh de la clase CFrameQuad.

El archivo de clase debe incluir el archivo de clase principal, y debe además ser heredado de él:

//+------------------------------------------------------------------+
//|                                                    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;   }

//+------------------------------------------------------------------+

En la sección privada de la clase, declaramos las variables de miembro de clase en las que almacenaremos las coordenadas y los tamaños del rectángulo que encierra la figura dibujada; estas son solo las coordenadas y el tamaño del área guardada del dibujo de la parte del fondo que se sobrescribe con la figura dibujada.

En el constructor paramétrico, transmitimos el identificador del objeto que se está creando y el puntero al elemento gráfico a partir del cual se crea dicho objeto. En la lista de inicialización del constructor, transmitimos al constructor de la clase padre el identificador transmitido en los argumentos del método, los parámetros predeterminados de las coordenadas y los tamaños de fotograma, además del puntero al elemento gráfico. En el cuerpo del constructor, establecemos el punto de anclaje del formulario dibujado como "arriba a la izquierda"; esto será necesario para calcular el desplazamiento del área copiada. Con este valor, los puntos de anclaje del desplazamiento de las coordenadas X e Y serán igual a cero.

Como hay muchos métodos de dibujado en la clase CCanvas, declararemos en la sección pública de esta clase todos los métodos correspondientes para dibujar figuras en el fondo del objeto de formulario, con la posterior restauración del fondo:

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
  };
//+------------------------------------------------------------------+

La implementación de todos estos métodos, cada uno individualmente, resultará similar a la implementación de otros métodos de dibujado similares, pero casi todos ellos tendrá sus propios matices inherentes a su método de dibujado (los tamaños del área guardada pueden diferenciarse en dos métodos de dibujado similares debido a las peculiaridades de cada método individual).

Veamos la implementación de estos métodos.

Método que establece el color del punto con las coordenadas especificadas:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

La lógica del método ha sido comentada con suficiente detalle en el código. Vamos a aclarar algunas cosas. Aquí, en primer lugar, establecemos las coordenadas X e Y del borde superior izquierdo del área de fondo rectangular que debemos almacenar en una matriz para luego restaurar el fondo debajo del punto dibujado. Como este es solo un punto (un píxel de la imagen), las coordenadas del área guardada coinciden con las coordenadas del punto que se está dibujando, y las dimensiones corresponden a las dimensiones de un píxel, es decir, 1 x 1.

A continuación, primero comprobamos si el fondo se ha guardado previamente (según el tamaño distinto a cero de la matriz en la que se guarda este fondo), y si el fondo se ha guardado antes, primero restauramos el fondo previamente guardado del objeto de formulario (en este caso, las coordenadas y el tamaño del área restaurada ya se han escrito en las variables de la clase). Después de restaurar con éxito el fondo previamente guardado, guardamos el fondo del formulario en las nuevas coordenadas en las que se dibujará el punto, y lo dibujamos. Después de eso, guardamos en las variables de clase las nuevas coordenadas, los tamaños del área guardada y los desplazamientos, para restaurar posteriormente el fondo borrado por el punto recién dibujado.

Método para dibujar un segmento de una línea vertical:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, el el cálculo de las coordenadas y los tamaños del rectángulo que describe la figura ya se distingue de dicho cálculo en el método anterior. Y esto es natural: aquí estaremos dibujando una línea vertical de un píxel de anchura. Pero la altura de esta línea ya debería calcularse como la diferencia entre los valores máximo y mínimo de las dos coordenadas Y de esta línea. La coordenada Y del área que se va a guardar debe coincidir con el valor mínimo de las dos coordenadas Y (el punto más alto de la línea que se está dibujando).

Método para dibujar un segmento de línea horizontal:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, el calculo de las coordenadas y tamaños del área guardada es semejante al mismo cálculo del método anterior, pero con la diferencia de que es una línea horizontal, y aquí la altura es igual a un píxel: debemos calcular la anchura y la coordenada X del área guardada.

Método que dibuja un segmento de una línea aleatoria:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, las coordenadas y el tamaño del área guardada ya se calculan a partir de las coordenadas de la línea dibujada.

Metodo que dibuja la línea quebrada:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, el cálculo de las coordenadas y los tamaños del área guardada tiene el mismo sentido, pero se distingue en la ejecución de los métodos anteriores, porque para una línea quebrada (como para muchas otras formas) las coordenadas se transmiten no como variables, sino en matrices: aquí no podemos saber de antemano cuántas rupturas tendrá la línea y cuántas coordenadas se transmitirán en los argumentos del método. Por consiguiente, antes de llamar al método, deberemos completar las dos matrices con las coordenadas X y las coordenadas Y correspondientes a cada punto de ruptura de la línea.
En el método, obtenemos los valores máximo y mínimo de estas matrices usando la función previamente analizada que retorna de la matriz transmitida el valor mínimo o máximo escrito en la misma. A continuación, los valores obtenidos se usan para calcular las coordenadas y los tamaños del área guardada del fondo del formulario.

Los demás métodos para dibujar figuras sin suavizado (preste atención solo al calcular las coordenadas y tamaños del área guardada):

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+


Vamos a analizar los metodos para dibujar primitivas coloreadas sin suavizado.

Metodo que colorea un área:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Como el método dibuja un área cerrada aleatoria, el tamaño del área guardada no se conoce de antemano. Por lo tanto, aquí deberá conservarse todo el formulario. Para hacer esto, estableceremos las coordenadas y las dimensiones como cero. Con estos valores, el método que guarda el área rectangular de la imagen almacenará en la matriz la imagen del fondo del formulario al completo.

El resto de los métodos de dibujado para primitivas coloreadas, el cálculo de las coordenadas de los mismos y los tamaños del área guardada, coinciden con el cálculo en los métodos de dibujado similares para las primitivas simples sin suavizar que hemos analizado anteriormente. Veamos los métodos como son:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+


Metodos de dibujado de prmitivas con suavizado.

Método que dibuja un punto usando el algoritmo de suavizado AntiAliasing:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, el cálculo de las coordenadas y los tamaños del área guardada se distingue de dicho cálculo en el método para dibujar un punto sin suavizado. El punto suavizado se puede ubicar en los tres píxeles adyacentes (9 en total, es decir, 3 x 3 píxeles), por consiguiente, las dimensiones del área guardada deberán ser de tres píxeles de alto y tres de ancho. Las coordenadas X e Y, respectivamente, deberán encontrarse un píxel a la izquierda y por encima de las coordenadas del punto en sí. Por consiguiente, el rectángulo del área guardada que dibuja el punto tendrá un margen de un píxel en todos los lados del punto que se está dibujando en caso de que el punto dibujado sea emborronado por el algoritmo de suavizado y dibujado en más de un píxel. Así, evitaremos restaurar de forma incompleta el fondo emborronado por el punto dibujado con el suavizado.

Método que dibuja un segmento de una línea aleatoria usando el algoritmo de suavizado:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

La prueba de este método ha mostrado que los bordes de la línea dibujada no están borrosos, por lo que aquí el cálculo del tamaño del área almacenada coincidirá con el cálculo en el método para dibujar una línea sin suavizado.

Método que dibuja un segmento de una línea aleatoria usando el algoritmo de suavizado de Wu:

//+------------------------------------------------------------------+
//| Draw a segment of a freehand line using the                      |
//| Wu algorithm                                                     |
//+------------------------------------------------------------------+
bool CFrameQuad::DrawLineWuOnBG(const int   x1,             // X coordinate of the segment first point
                                const int   y1,             // Y coordinate of the segment first point
                                const int   x2,             // X coordinate of the segment second point
                                const int   y2,             // Y coordinate of the segment second point
                                const color clr,            // Color
                                const uchar opacity=255,    // Opacity
                                const bool  redraw=false,   // Chart redraw flag
                                const uint  style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration's values or a custom value
  {
//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
//--- Set the width and height of the image outlining the rectangle (to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+1;
   this.m_quad_height=::fabs(y2-y1)+1;
      
//--- Calculate coordinate offsets for the saved area depending on the anchor point
   int shift_x=0,shift_y=0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
//--- If the pixel array is not empty, the background under the image has already been saved -
//--- restore the previously saved background (by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev)))
         return false;
     }
//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(int(this.m_quad_x+shift_x),int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      return false;
//--- Draw the shape and update the element
   this.m_element.DrawLineWu(x1,y1,x2,y2,clr,opacity,style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   return true;
  }
//+------------------------------------------------------------------+

Aqui, el cálculo es similar al método anterior por el mismo motivo.

Método que dibuja un segmento de una línea aleatoria con el grosor indicado usando el algoritmo de suavizado con filtrado preliminar:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

En este método, el cálculo del área guardada ya es diferente de todos los anteriores. Como para las líneas con un algoritmo de suavizado de este tipo se especifica una anchura y una apariencia para sus extremos, la anchura del área guardada también debera considerar el tamaño (grosor) de la línea y sus bordes (los bordes de la línea pueden ser redondeados, por lo que el tamaño (longitud) de la línea aumentará en dos radios de redondeo, es decir, en la magnitud especificada como anchura de la línea).

Método que dibuja un segmento vertical de una línea aleatoria con el grosor indicado usando el algoritmo de suavizado con filtrado preliminar:

//+----------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, el cálculo es igual que el descrito para el método anterior.

A continuación, podemos ver los demás métodos de dibujado de primitivas suavizadas y de otro tipo:

//+-----------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Aquí, para todos los métodos, los algoritmos de cálculo del área de fondo almacenada son casi idénticos a los algoritmos de cálculo en los métodos anteriores.

Querríamos aclarar que en los métodos de dibujado de elipses (DrawEllipseAAOnBG y DrawEllipseWuOnBG), si el tamaño del rectángulo dentro del cual se dibuja la elipse es inferior a tres píxeles, la figura no se dibujará. Por consiguiente, aquí, en los cálculos, merece la pena comprobar si el tamaño es inferior a tres píxeles. Por el momento, no hemos logrado averiguar si se trata de un malentendido relacionado con el dibujado de elipses, o si en realidad esta lógica se encuentra incorporada en los métodos de la clase CCanvas. Esperamos descubrirlo en el futuro.

Ya hemos creado todas las clases de los objetos de fotograma que necesitamos por hoy.

Ahora, necesitamos crear una clase en la que podamos almacenar los objetos de fotograma de animación creados, acceder a ellos y gestionarlos.

De momento, la clase contendrá dos listas para almacenar los objetos de fotograma de animación creados (de texto y rectangulares), y contendrá los métodos para crear los nuevos objetos y gestionarlos. Como consecuencia de ello, en esta clase se almacenarán todos los objetos de animación pertenecientes al mismo formulario. Por consiguiente, cada formulario tendrá su propio conjunto de objetos de animación que podrán crearse dinámicamente y añadirse a la lista de animaciones del formulario.

Clase de animaciones del formulario

En la carpeta \MQL5\Include\DoEasy\Objects\Graph\Animations\, creamos un nuevo archivo Animations.mqh de la clase CAnimations.

El archivo de clase debe incluir los archivos de clase recién creados que heredan del objeto básico de fotograma de animación, mientras que la propia clase deberá heredar del objeto básico de la biblioteca estándar CObject:

//+------------------------------------------------------------------+
//|                                                   Animations.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "FrameText.mqh"
#include "FrameQuad.mqh"
//+------------------------------------------------------------------+
//| Pixel copier class                                               |
//+------------------------------------------------------------------+
class CAnimations : public CObject
  {
  }

En la sección privada de la clase, declaramos el puntero al objeto de elemento gráfico que se usará para crear los objetos de animación, las dos listas para almacenar los dos tipos de objetos de fotograma de animación y los métodos para retornar la bandera que indica la presencia del objeto especificado en la lista y para retornar el puntero a un objeto de fotograma de animación existente, o bien crear previamente este, si no se encuentra en la lista:

//+------------------------------------------------------------------+
//| 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:

Veremos todos estos métodos a continuación.

En la sección pública de la clase, declararemos los métodos necesarios para crear objetos y trabajar con ellos en las listas, así como los métodos para dibujar primitivas con guardado y restauracion del fondo:

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
  };
//+------------------------------------------------------------------+


Veamos la implementación de los métodos declarados.

Constructor paramétrico:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CAnimations::CAnimations(CGCnvElement *element)
  {
   this.m_element=element;
  }
//+------------------------------------------------------------------+

Aquí, asignamos al puntero m_element transmitido en los argumentos el valor del puntero al objeto de elemento gráfico.

Método que retorna el puntero a un objeto de fotograma de animación según el tipo y el identificador:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

La lógica del método se describe detalladamente en los comentarios al código, y a buen seguro no requerirá de aclaraciones.

Método que retorna la bandera sobre la presencia en la lista del objeto de fotograma de animación con el tipo y el identificador especificados:

//+------------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

El método retorna el resultado de tipo bool de la llamada al método GetFrame() anteriormente analizado. Si el método GetFrame() ha retornado un resultado distinto a NULL (es decir, el objeto buscado se encuentra en la lista), el método retornará true, de lo contrario, false.

Método que crea un nuevo objeto de fotograma de animación de texto:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

La lógica del método se describe al completo en los comentarios al código.

Método que crea un nuevo objeto de fotograma de animación rectangular:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

El método es idéntico al anterior, está igualmente comentado al completo y no necesita explicaciones adicionales.

Método que retorna un puntero o que crea un nuevo objeto de fotograma de animación:

//+------------------------------------------------------------------+
//| 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;
     }
  }
//+------------------------------------------------------------------+

La lógica del método se describe en los comentarios al código. Si necesitamos trabajar con un fotograma de animación, podemos crearlo de antemano, asignarle un puntero y controlar el objeto resultante. Pero si necesitamos crear objetos dinámicamente, este método permitirá, en ausencia de un objeto con el identificador especificado, crear primero un nuevo objeto y luego retornarle un puntero. Por consiguiente, podemos organizar la creación dinámica de objetos "según la situación", obtener inmediatamente un puntero y trabajar con él.

Métodos para trabajar con objetos de fotograma de animación.

Método que muestra el texto en el fondo, con almacenamiento y restauración del fondo

//+--------------------------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

Transmitimos al método el identificador del objeto, los parámetros del texto mostrado (el propio texto, las coordenadas X e Y, el punto de anclaje, el color y la opacidad), la bandera que indica la necesidad de crear un nuevo objeto con el identificador especificado si el objeto con dicho identificador no se encuentra en la lista, y la bandera de redibujado del gráfico.
A continuación, obtenemos el puntero al objeto necesario (o creamos un objeto, si no hay ninguno). Si no hemos podido obtener el puntero, retornaremos false.
Si hemos obtenido el puntero, retornaremos el resultado del funcionamiento del método TextOnBG() del objeto de fotograma de animación de texto resultante.

Método que establece el color del punto con las coordenadas especificadas:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

La lógica del método es idéntica a la lógica del método analizado anteriormente. Transmitimos al método el identificador del objeto, las coordenadas X e Y de la figura dibujada, su color, la bandera que indica la necesidad de crear un nuevo objeto con el identificador especificado si el objeto con dicho identificador no se encuentra en la lista, y la bandera de redibujado del gráfico.
A continuación, obtenemos el puntero al objeto necesario (o creamos un objeto, si no hay ninguno). Si no hemos podido obtener el puntero, retornaremos false.
Si hemos obtenido el puntero, retornaremos el resultado del funcionamiento del método SetPixelOnBG() del objeto de fotograma de animación rectangular resultante.

Otros métodos para dibujar primitivas.

La lógica de los demás métodos para dibujar figuras es idéntica a la lógica de los métodos anteriores. Veamos su listado:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+


Entonces, la clase creada de objetos de animación debería ser un componente del objeto de formulario. De esta manera, cada formulario tendrá sus propios métodos de dibujado únicos para cada objeto de formulario.

Abrimos el archivo de la clase de objeto de formulario \MQL5\Include\DoEasy\Objects\Graph\Form.mqh e introducimos las modificaciones necesarias.

A continuación, le añadimos el archivo de la clase del objeto de animación:

//+------------------------------------------------------------------+
//|                                                         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"
//+------------------------------------------------------------------+

Eliminamos del listado la clase del objeto de copiador de píxeles (lo hemos trasladado a otro archivo):

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GCnvElement.mqh"
#include "ShadowObj.mqh"
#include "Animations\Animations.mqh"
//+------------------------------------------------------------------+
//| Pixel copier class                                               |
//+------------------------------------------------------------------+
class CPixelCopier : public CObject
  {
private:
...
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+

En la sección privada de la clase, en lugar de la lista del copiador de píxeles

CArrayObj         m_list_pc_obj;                            // List of pixel copier objects

declaramos el puntero al objeto de clase animaciones:

//+------------------------------------------------------------------+
//|                                                         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:

Además, eliminamos de la sección privada de clase la declaración del método IsPresentPC(), ya innecesario; asimismo, eliminamos su implementación del listado del código:

//--- 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:

A continuación, eliminamos de la seccion pública de la clase el método ya innecesario:

//--- 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;      }

y terminamos de escribir los métodos que retornan los punteros al objeto de animaciones y a las listas de los fotogramas animación de texto y rectangulares:

   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

Eliminamos la declaración del método para crear un nuevo objeto de copiador de píxeles:

//--- 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

También eliminamos la implementación del método escrito fuera del cuerpo de la clase.

En la sección pública de la clase, en el bloque de métodos para trabajar con los píxeles de la imagen, escribiremos los nuevos métodos para crear los objetos de fotograma de animación y para retornar los punteros a los objetos y los métodos de dibujado creados, con guardado y restauración del fondo:

//+------------------------------------------------------------------+
//| 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);  }
                       
//+------------------------------------------------------------------+

Todos estos métodos retornan el resultado del funcionamiento de los métodos correspondientes del objeto de animaciones CAnimations que hemos creado anteriormente.

En el destructor de la clase, eliminamos el objeto de animaciones:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CForm::~CForm()
  {
   if(this.m_shadow_obj!=NULL)
      delete this.m_shadow_obj;
   if(this.m_animations!=NULL)
      delete this.m_animations;
  }
//+------------------------------------------------------------------+

En el metodo de inicialización, creamos el nuevo objeto de animaciones:

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

Ahora, al crear un nuevo objeto de formulario, se creará automáticamente un objeto de animación para él, en el que podremos añadir dinámicamente nuevos fotogramas de animaciones, o crear otros predefinidos durante la creación del formulario. Al final del programa, el objeto de animación se destruirá para evitar pérdidas de memoria.

Ya estamos listos para poner a prueba las clases creadas.

Qué vamos a probar y cómo vamos a hacerlo. Tenemos un asesor experto de prueba que dibuja cuatro formularios. Al clicar en el cuarto formulario, el inferior, la inscripción "H-Gradient" se mueve, indicando el método de rellenado (horizontal). Ahora, vamos a crear y desplazar esta inscripción como un objeto de fotograma de animación de texto. El tercer formulario, con un rellenado vertical, se usará para mostrar varias figuras dibujadas. Pero antes de dibujar la figura, primero mostraremos en el formulario el texto como un fotograma de texto de animación. Dibujaremos las figuras por encima. Vamos a organizar la alternancia entre los tipos de figuras dibujadas presionando las teclas del teclado; el texto mostrará la figura dibujada seleccionada. Cada clic del ratón en el formulario cambiará las coordenadas de los puntos de la figura dibujada.

Simulación

Para la simulación, vamos a tomar el asesor del artículo anterior y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part79\ con el nuevo nombre TestDoEasyPart79.mq5.

En el área de variables globales, escribiremos las macrosustituciones necesarias para especificar las coordenadas iniciales de las figuras dibujadas y declararemos las variables para almacenar las coordenadas de los diferentes puntos de las figuras dibujadas (usaremos cinco puntos en total) y los valores de sus cambios:

//+------------------------------------------------------------------+
//|                                             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;
//+------------------------------------------------------------------+

En el manejador OnInit(), en los bloques para crear los dos últimos formularios, ahora crearemos un texto usando los objetos de animación de texto:

      //--- 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

Estos objetos de texto de aquí ya los hemos creado antes. Crearemos las figuras dibujadas de forma dinámica, clicando con el ratón en el área del formulario.

Para procesar las pulsaciones sobre las teclas de los teclados, añadiremos al manejador OnChartEvent() este bloque de código:

//--- 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

Vamos a modificar ligeramente el código en el bloque para procesar los clics en los formularios; ahora, mostraremos el texto usando los objetos de animación de texto del formulario en el que se ha clicado:

//--- 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;
              }
           }
        }
     }

Clicando en el tercer formulario (con el identificador 2), realizaremos el procesamiento en la función FigureProcessing(), clicando en el cuarto formulario (con el identificador 3), como antes, determinaremos el ángulo de anclaje del texto en función del valor de la variable n y mostraremos el texto. Pero el texto ahora se mostrará usando la clase de objeto de fotograma de animación de texto.

Función auxiliar que retorna el tipo de figura según el botón del teclado que presionemos:

//+------------------------------------------------------------------+
//| 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;
     }
  }
//+------------------------------------------------------------------+

La función en la que procesamos el clic sobre el objeto de formulario es bastante grande. Mejor dicho, es simple, pero voluminosa, porque en ella (en el operador switch) se procesa cada botón presionado que se corresponde con la figura dibujada. El procesamiento es simple: establecemos las coordenadas iniciales, verificamos que superen el rango de valores permitido, dibujamos una figura con estos parámetros y aumentamos el desplazamiento de los valores de los parámetros. La próxima vez que cliquemos sobre el formulario, la figura anterior será sobrescrita por el fondo restaurado y la nueva figura se dibujará en las coordenadas nuevas de sus puntos.

//+------------------------------------------------------------------+
//| 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;
     }
  }
//+------------------------------------------------------------------+

Esperamos que el código de esta función resulte sencillo al lector y no despierte en él ninguna duda. En cualquier caso, podrá formular cualquier pregunta al respecto en los comentarios al artículo.

Para la prueba simple, hemos decidido no crear el procesamiento general de parámetros idénticos. Resulta más fácil y rápido escribir todo yendo "al grano" en los casos del operador switch. Lo importante para nosotros es comprobar la funcionalidad de las clases creadas, sin importar que el código resulte repetitivo.

Compilamos el asesor y lo iniciamos en el gráfico. Después de comenzar, presionamos varios botones del teclado para asegurarnos de que el modo de dibujado cambie y se muestre en la inscripción en el tercer formulario. Si clicamos en el cuarto formulario, el texto se moverá de la misma forma que en el asesor del artículo anterior, solo que ahora este texto se representará a través de la clase del objeto de fotograma de texto de animación.

Si, después de seleccionar el modo de dibujado necesario, comenzamos a clicar en el tercer formulario (en el que se muestra la inscripción con la descripción del modo de dibujado), sobre esta inscripción y el formulario en sí, la figura seleccionada se dibujará cada vez que cambien los valores de las coordenadas de sus puntos.


No todos los métodos de dibujado han funcionado bien. Por ejemplo, el método FillPolygon() de la clase CCanvas, si usamos los parámetros iniciales en nuestra función para procesar los clics en un objeto, simplemente cae en un ciclo infinito, mientras que el método EllipseAA() genera un error de división por cero. Este método tiene  puntos potenciales donde podría suceder esto:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Informamos sobre este problema en el hilo del perfil. Si bien no hay respuesta por parte de los desarrolladores, intentaremos solucionar estos errores en artículos posteriores.

Partiendo de lo anterior, algunos métodos de dibujado no se llaman en el manejador de los clics del ratón sobre el objeto de formulario, en su lugar, la función simplemente genera un retorno. En cualquier caso, esto se corregirá en el futuro. No obstante, ahora podemos ver que el fondo debajo de las figuras dibujadas se restaura, y que todo se muestra como esperábamos. Hay un problema al cambiar los modos de dibujado: después de cambiarlos, el fondo no se restaura. Pero esto ya no se debe a ningún error, sino a la falta de un método que borre la imagen y restaure el fondo. Lo crearemos en artículos posteriores, pero aquí, para realizar las pruebas y mostrar un ejemplo, simplemente cambiaremos el marco temporal para restaurar completamente el fondo antes de mostrar otra figura en el formulario.


¿Qué es lo próximo?

En el próximo artículo, continuaremos desarrollando las clases de animación.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y el archivo del asesor de prueba para MQL5. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

*Artículos de esta serie:

Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico
Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas
Gráficos en la biblioteca DoEasy (Parte 75): Métodos de trabajo con primitivas y texto en el elemento gráfico básico.
Gráficos en la biblioteca DoEasy (Parte 76): Objeto de formulario y temas de color predeterminados
Gráficos en la biblioteca DoEasy (Parte 77): Clase de objeto Sombra
Gráficos en la biblioteca DoEasy (Parte 78): Fundamentos de animación en la biblioteca. Cortando las imágenes

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9652

Archivos adjuntos |
MQL5.zip (4076.37 KB)
Cómo ser un mejor programador (parte 02): 5 cosas que evitar para convertirse en un programador exitoso de MQL5 Cómo ser un mejor programador (parte 02): 5 cosas que evitar para convertirse en un programador exitoso de MQL5
Este es un artículo de lectura obligada para cualquiera que desee mejorar su carrera como programador. Esta serie de artículos tiene como objetivo convertirlo a usted en el mejor programador posible, sin importar la experiencia que tenga. Las ideas analizadas funcionan tanto para principiantes como para profesionales de la programación en MQL5.
Cómo ser un mejor programador (parte 01): 5 cosas que evitar para convertirse en un programador exitoso de MQL5 Cómo ser un mejor programador (parte 01): 5 cosas que evitar para convertirse en un programador exitoso de MQL5
Hay muchos malos hábitos que impiden a los programadores principiantes e incluso avanzados sacar el cien por cien de rendimiento a su carrera de codificación. En este artículo, discutiremos y abordaremos dichos hábitos. El presente material es una lectura obligada para todos aquellos que quieran convertirse en desarrolladores exitosos en MQL5.
Gráficos en la biblioteca DoEasy (Parte 80): Clase de objeto "Fotograma de animación geométrica" Gráficos en la biblioteca DoEasy (Parte 80): Clase de objeto "Fotograma de animación geométrica"
En este artículo, optimizaremos el código de las clases de los artículos anteriores y crearemos una clase de objeto de fotograma de animación geométrica que nos permitará dibujar polígonos regulares con un número determinado de vértices.
Gráficos en la biblioteca DoEasy (Parte 78): Fundamentos de animación en la biblioteca. Cortando las imágenes Gráficos en la biblioteca DoEasy (Parte 78): Fundamentos de animación en la biblioteca. Cortando las imágenes
En el artículo, definiremos los principios de animación que se usarán en algunas partes de la biblioteca, y también desarrollaremos una clase para copiar una parte de una imagen y pegarla en un lugar específico del objeto de formulario, con la posibilidad de guardar y restaurar la parte del fondo del formulario sobre la que se superpondrá la imagen.