Gráficos na biblioteca DoEasy (Parte 79): classe para o objeto quadro-de-animação e seus objetos herdeiros
Sumário
- Ideia
- Aprimorando as classes da biblioteca
- Classe para o objeto quadro de animação
- Classe para o quadro de animação de texto
- Classe para o quadro de animação retangular
- Classe para as animações da forma
- Teste
- O que vem agora?
Ideia
No último artigo, criamos uma classe para salvar e restaurar a parte do fundo que estava sob a forma desenhada. Hoje daremos continuação a esta ideia e criaremos várias classes com base nela, incluindo a classe base de um quadro de animação e de seus descendentes - a classe do quadro de animação de texto e a classe do quadro de animação retangular.
A classe base conterá um conjunto comum de propriedades para um quadro de animação, e seus descendentes terão seus próprios métodos para desenhar formas. A classe da animação de texto permitirá trabalhar com textos, já a classe do quadro de animação retangular possibilitará criar um quadro de animação e desenhar formas nele usando métodos de desenho baseados na classe CCanvas.
Cada objeto-forma criado terá um conjunto de métodos para desenhar na sua tela, o que permitirá criar rapidamente novas imagens na forma e gerenciá-las. Para usar convenientemente a caixa de ferramentas de desenho em cada forma, criaremos uma classe geral que conterá listas de todos os desenhos de texto e de figuras criados na forma (mais tarde adicionaremos novos métodos de animação, além disso, suas listas também estarão em esta classe geral). Esta abordagem nos permitirá gerar dinamicamente novas imagens e salvá-las nos tipos de lista correspondentes, para ser rapidamente encontradas no objeto-forma e exibidas no fundo. Além disso, esses objetos salvarão automaticamente o fundo da forma, e, quando forem excluídos, alterados ou movidos, o fundo salvo será restaurado.
Assim, hoje faremos uma pequena revisão das classes de desenho criadas nos artigos anteriores, desenvolveremos uma classe para o objeto-quadro de animação base e desenvolveremos duas classes para seus descendentes - uma de quadro de animação de texto e outra de quadro de animação retangular. Criaremos uma classe para armazenar listas desses objetos-quadros e facilitaremos o trabalho com elas a partir do objeto-forma.
Aprimorando as classes da biblioteca
Para começar, modificaremos as classes da biblioteca criadas anteriormente. No arquivo \MQL5\Include\DoEasy\Defines.mqh incluímos a lista de tipos de quadros de animação e a lista de tipos de figuras desenhadas na classe do quadro de animação retangular:
//+------------------------------------------------------------------+ //| 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, // a Vertical segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_LINE_HORIZONTAL, // Horizontal line FIGURE_TYPE_LINE_HORIZONTAL_THICK, // Horizontal segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_LINE, // Arbitrary line FIGURE_TYPE_LINE_AA, // Line with antialiasing FIGURE_TYPE_LINE_WU, // Line with WU smoothing FIGURE_TYPE_LINE_THICK, // Segment of a freehand line having a specified width using antialiasing algorithm FIGURE_TYPE_POLYLINE, // Polyline FIGURE_TYPE_POLYLINE_AA, // Polyline with antialiasing FIGURE_TYPE_POLYLINE_WU, // Polyline with WU smoothing FIGURE_TYPE_POLYLINE_SMOOTH, // Polyline with a specified width using two smoothing algorithms FIGURE_TYPE_POLYLINE_THICK, // Polyline with a specified width using a smoothing algorithm FIGURE_TYPE_POLYGON, // Polygon FIGURE_TYPE_POLYGON_FILL, // Filled polygon FIGURE_TYPE_POLYGON_AA, // Polygon with antialiasing FIGURE_TYPE_POLYGON_WU, // Polygon with WU smoothing FIGURE_TYPE_POLYGON_SMOOTH, // Polygon with a specified width using two smoothing algorithms FIGURE_TYPE_POLYGON_THICK, // Polygon with a specified width using a smoothing algorithm FIGURE_TYPE_RECTANGLE, // Rectangle FIGURE_TYPE_RECTANGLE_FILL, // Filled rectangle FIGURE_TYPE_CIRCLE, // Circle FIGURE_TYPE_CIRCLE_FILL, // Filled circle FIGURE_TYPE_CIRCLE_AA, // Circle with antialiasing FIGURE_TYPE_CIRCLE_WU, // Circle with WU smoothing FIGURE_TYPE_TRIANGLE, // Triangle FIGURE_TYPE_TRIANGLE_FILL, // Filled triangle FIGURE_TYPE_TRIANGLE_AA, // Triangle with antialiasing FIGURE_TYPE_TRIANGLE_WU, // Triangle with WU smoothing FIGURE_TYPE_ELLIPSE, // Ellipse FIGURE_TYPE_ELLIPSE_FILL, // Filled ellipse FIGURE_TYPE_ELLIPSE_AA, // Ellipse with antialiasing FIGURE_TYPE_ELLIPSE_WU, // Ellipse with WU smoothing FIGURE_TYPE_ARC, // Ellipse arc FIGURE_TYPE_PIE, // Ellipse sector }; //+------------------------------------------------------------------+
Usaremos os tipos de quadros de animação para identificar objetos-quadros de animação (sejam eles textos, figuras ou outro tipo de quadro de animação que faremos mais adiante em artigos futuros). Os tipos de formas a serem desenhadas nos dirão o que exatamente está sendo desenhado no quadro de animação retangular. Esses tipos corresponderão aos métodos de desenho disponíveis na classe CCanvas (seções "Acesso aos dados", "Desenho de primitivas", "Desenho de primitivas sombreadas" e "Desenho de primitivas usando suavização" na tabela de métodos da classe).
No arquivo \MQL5\Include\DoEasy\Data.mqh incluímos os índices das novas mensagens:
//--- 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
e os textos que correspondem aos índices recém-adicionados:
//--- 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"},
No arquivo de funções de serviço da biblioteca \MQL5\Include\DoEasy\Services\DELib.mqh incluímos funções que retornam os valores máximo e mínimo na 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; } //+------------------------------------------------------------------+
As funções retornam o valor máximo ou mínimo na matriz passada a elas por referência e, além disso, são do tipo bool porque qualquer valor retornado desde a função como "errôneo" pode estar nas células da matriz. Por exemplo, se, após um erro ao receber dados de uma matriz, retornar -1 (como é feito em muitas funções), esse valor pode ser um dos escritos na matriz. Ao retornar um valor encontrado corretamente (-1), nosso programa irá assumir que isso é um erro. O que não é verdade. Por esse motivo, em caso de erro, retornaremos false, e escreveremos numa variável passada por referência à função o valor máximo ou mínimo encontrado na própria matriz. Se a função retornar true, essa variável armazenará o valor desejado. Na variável source, à função é passado o nome do método desde o qual esta última é chamada, o que permite exibir o nome desse método e o corpo da mensagem do erro.
Também adicionaremos nesse local uma função que retornará uma descrição do tipo da figura desenhada:
//+------------------------------------------------------------------+ //| Return the description of the drawn shape type | //+------------------------------------------------------------------+ string FigureTypeDescription(const ENUM_FIGURE_TYPE figure_type) { return(StringSubstr(EnumToString(figure_type),12)); } //+------------------------------------------------------------------+
Aqui, o tipo passado à função como enumeração é convertido numa string. E a partir da representação textual do tipo é extraída uma substring desde o 12º caractere para cortar o texto desnecessário. Assim, por exemplo, o tipo de figura FIGURE_TYPE_TRIANGLE será convertido no texto "FIGURE_TYPE_TRIANGLE" e deste texto será extraída a substring desejada a partir do 12º caractere, "FIGURE_TYPE_TRIANGLE". Como resultado, será retornada a string "TRIANGLE".
No último artigo, ao criar métodos que permitiam copiar para uma matriz uma parte do fundo, determinamos o tamanho e as coordenadas do retângulo de fundo copiado com base no tamanho do texto que era exibido na forma. Hoje exibir não só textos, mas também imagens. E como seus tamanhos não serão mais determinados com base no tamanho do texto, precisamos criar um método que determine as coordenadas e o tamanho do retângulo copiado da parte do fundo.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh da classe do elemento gráfico renomeamos o 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
Agora este método será chamado GetShiftXYbyText() e declararemos o novo método que retornará as coordenadas e o tamanho da parte copiada da imagem, retorno esse feito com base no tamanho especificado em relação ao ponto de ancoragem do 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
Escreveremos sua implementação no final da listagem da classe.
Método que retorna - com base no tamanho - os deslocamentos de coordenadas em relação ao ponto de ancoragem do retângulo:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Aqui, dependendo da largura e altura da área copiada, bem como do ponto de ancoragem passado ao método, calculamos os deslocamentos de coordenadas em relação ao ponto de ancoragem e os escrevemos nas variáveis passadas por referência ao método.
Método que retorna deslocamentos de coordenadas em relação ao ponto de ancoragem do 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); } //+------------------------------------------------------------------+
Aqui, primeiro determinamos o tamanho do texto, passado ao método, e, em seguida, chamamos o método acima para determinar o deslocamento das coordenadas da área salva da imagem de fundo da forma.
No último artigo, desenvolvemos uma classe para copiar uma parte da imagem de fundo da forma e restaurá-la a partir de uma matriz. Colocamos essa classe no arquivo da classe do objeto-forma. Hoje removeremos essa classe do arquivo que contém a classe do objeto-forma e iremos movê-la para um novo arquivo da classe recém-criada do objeto-quadro (mais uma vez estou convencido de que é melhor escrever e armazenar classes em arquivos separados).
Então, vamos criar uma classe base para um quadro de animação. A classe conterá propriedades comuns a todos os seus descendentes.
Classe para o objeto quadro de animação
No arquivo \MQL5\Include\DoEasy\Objects\Graph\ criamos uma nova pasta Animations\ e, nela, o novo arquivo Frame.mqh da classe CFrame.
Ao arquivo da classe deve ser anexado o arquivo da classe do objeto-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" //+------------------------------------------------------------------+
Em seguida, colocamos a classe do objeto-copiador de pixels (considerado no último artigo), classe essa removida do arquivo da classe do objeto-forma:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
A única coisa que muda (temporariamente) aqui é que nós comentamos as linhas de código que devem copiar todo o desenho da forma salvo anteriormente. Aqui foi cometido um erro, e o fundo não foi copiado da matriz do objeto-forma, mas diretamente do recurso gráfico, e contém apenas todas as alterações feitas na imagem do fundo-forma. Para corrigir isso, depois de criada a janela, precisaremos salvá-la numa matriz especialmente criada para isso, ela armazenará uma cópia da imagem original da forma. Temos essa matriz, só que ainda não criamos métodos para preservar a aparência original da forma imediatamente após sua criação. Para fazer isso, precisaremos pensar e escrever esses métodos. Assim, embora não tenhamos esses métodos, comentamos essas linhas, e a restauração do fundo, que tem as dimensões de toda a forma (e não de sua parte), será realizada no ciclo de restauração de parte do fundo da forma (isto é, não copiando uma matriz para outra, e preenchendo o fundo da forma com elementos da matriz no qual a cópia salva da parte da imagem do fundo da forma é armazenada).
Em seguida, depois de listar a classe-copiador de pixels, escrevemos o corpo da classe de objeto-quadro de animação:
//+------------------------------------------------------------------+ //| 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); }; //+------------------------------------------------------------------+
A classe é herdada da classe do objeto-copiador de pixels, ou seja, de fato, é ele.
Todas as variáveis e métodos declarados na classe são descritos nos comentários. Como essa classe será a base para outras classes-quadros de animação, aqui são escritas apenas todas as propriedades e métodos comuns aos descendentes.
Variáveis e métodos que retornam as últimas coordenadas, deslocamentos e pontos de ancoragem são necessários para que ao restaurar uma parte salva anteriormente da imagem de fundo da forma, possamos saber em quais coordenadas a última parte salva da imagem foi localizada, para que na mesma coordenadas com os mesmos deslocamentos em relação às âncoras de ponto seja colocado o fundo salvo que foi apagado pela imagem desenhada sobre ele.
A classe tem três construtores:
- construtor público padrão,
- construtor protegido do objeto-quadro de texto,
- construtor protegido do objeto-quadro retangular.
Consideremos a implementação de construtores protegidos.
Construtor de quadros retangulares:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Ao construtor é passado: o identificador do objeto-quadro retangular a ser criado, suas coordenadas X e Y, a largura e a altura do quadro e também ao construtor é passado o ponteiro para o objeto-elemento gráfico a partir do qual este novo objeto é criado. Como a classe é uma herdeira do objeto-copiador de pixels, na lista de inicialização do construtor passamos todos os parâmetros necessários para o construtor da classe base, e eles são todas as propriedades passadas nos argumentos do construtor.
No corpo da classe, definimos os parâmetros padrão para todas as variáveis-membro da classe.
Construtor de quadros 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); } //+------------------------------------------------------------------+
Ao construtor é passado: o identificador do objeto-quadro de texto a ser criado, suas coordenadas X e Y, o texto e o ponteiro para o objeto-elemento gráfico a partir do qual este novo objeto é criado.
No corpo da classe, primeiro determinamos o tamanho do texto, depois definimos os valores padrão para as variáveis-membros da classe e no objeto-copiador de pixels pai definimos o identificador do objeto criado, suas coordenadas e o tamanho do texto.
Criamos as classes de objetos herdeiros da classe do objeto-quadro de animação.
Classe para o quadro de animação de texto
Na pasta \MQL5\Include\DoEasy\Objects\Graph\Animations\ criamos o novo arquivo FrameText.mqh da classe CFrameText.
No arquivo deve estar integrado o arquivo de classe do quadro de animação; a classe em sim deve ser sua herdeira:
//+------------------------------------------------------------------+ //| 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) {} }; //+------------------------------------------------------------------+
Aqui vemos um método público para desenhar texto no fundo do objeto-forma e dois construtores - padrão e paramétrico.
Ao construtor paramétrico passamos o identificador do objeto-quadro de animação de texto a ser criado e um ponteiro para o elemento objeto-gráfico a partir do qual esse objeto é criado. À classe pai da lista de inicialização passamos o identificador passado nos argumentos do construtor, os valores padrão para coordenadas/texto e um ponteiro para o elemento gráfico, também passado nos argumentos do construtor.
Método que exibe texto no plano de fundo com armazenamento e restauração do plano de fundo:
//+--------------------------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
A lógica do método é descrita em detalhes nos comentários do código considerado no último artigo durante o teste (uma lógica semelhante foi escrita no manipulador OnChartEvent() do EA de teste). Após o texto ser desenhado na forma, seu ponto de ancoragem, as coordenadas X e Y e as dimensões de deslocamento em relação ao ponto de ancoragem são gravados nas variáveis da classe pai - seus valores serão usados para restaurar o fundo da imagem da forma apagado pelo texto.
Agora criamos uma segunda classe herdeiro do objeto-quadro de animação.
Classe para o quadro de animação retangular
Na pasta \MQL5\Include\DoEasy\Objects\Graph\Animations\ criamos o novo arquivo FrameQuad.mqh da classe CFrameQuad.
Ao arquivo da classe deve ser anexado o arquivo da classe pai, e ele deve ser herdado dela:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Na seção privada da classe, declararemos as variáveis dos membros da classe nas quais armazenaremos as coordenadas e tamanhos do retângulo que envolve a figura desenhada - essas são apenas as coordenadas e o tamanho da área salva do desenho do parte do plano de fundo que é apagada pela figura desenhada.
No construtor paramétrico, são passados o identificador do objeto a ser criado e o ponteiro para o elemento gráfico a partir do qual este objeto é criado. Na lista de inicialização do construtor, ao construtor da classe pai são passados o identificador passado nos argumentos do método, os parâmetros padrão das coordenadas e dos tamanhos do quadro e um ponteiro para o elemento gráfico. No corpo do construtor, definimos o ponto de ancoragem da forma desenhada como "canto superior esquerdo" - isso será necessário para calcular o deslocamento da área copiada. Com este valor, os pontos de ancoragem, os deslocamentos das coordenadas X e Y serão zero.
Como os métodos de desenho na classe CCanvas são muitos, na seção pública desta classe serão declarados todos os métodos para desenhar formas no plano de fundo do objeto-forma, com posterior restauração do plano de fundo:
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 }; //+------------------------------------------------------------------+
A implementação de todos esses métodos, cada um individualmente, será semelhante à implementação de outros métodos de desenho semelhantes, só que cada um deles terá nuances inerentes ao seu método de desenho (os tamanhos da área salva podem diferir em dois métodos de desenho semelhantes devido às peculiaridades de cada um deles).
Vejamos a implementação desses métodos.
Método que define a cor do ponto com as 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; } //+------------------------------------------------------------------+
A lógica do método é comentada em detalhes suficientes no código. Vou explicar um pouco mais detalhadamente. Aqui, primeiro definimos as coordenadas X e Y da borda superior esquerda da região de fundo retangular que precisa ser armazenada numa matriz para restaurar posteriormente o fundo localizado abaixo do ponto desenhado. Como isso é apenas um ponto (um pixel da imagem), as coordenadas da área salva coincidirão com as do ponto a ser desenhado, e as dimensões corresponderão às de um pixel, ou seja, 1 x 1.
Em seguida, primeiro, verificamos se o fundo foi salvo anteriormente (se o tamanho da matriz em que esse fundo é salvo é diferente de zero), e se for assim, restauramos o fundo do objeto-forma (neste caso, as coordenadas e o tamanho da área restaurada já foram gravados nas variáveis da classe). Após restaurar com sucesso o fundo salvo anteriormente, salvamos o fundo da forma nas novas coordenadas, nas quais o ponto será desenhado, e o desenhamos. Em seguida, nas variáveis da classe salvamos as novas coordenadas, os tamanhos da área salva e os deslocamentos - para a posterior restauração do fundo apagado pelo ponto recém-desenhado.
Método para desenhar um segmento de linha 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; } //+------------------------------------------------------------------+
Aqui o cálculo das coordenadas e dimensões do retângulo que contorna a figura difere deste cálculo no método anterior. E isso é normal porque aqui estamos desenhando uma linha vertical de um pixel de largura. Mas a altura desta linha deve ser calculada como a diferença entre os valores máximo e mínimo das duas coordenadas Y desta linha. A coordenada Y da região a ser salva deve corresponder ao valor mínimo das duas coordenadas Y (o ponto mais alto da linha que está sendo desenhada).
Método para desenhar um segmento de linha 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; } //+------------------------------------------------------------------+
Aqui o cálculo de coordenadas e tamanhos da área salva é semelhante ao do método anterior, mas só que é uma linha horizontal, que aqui a altura é igual a um pixel e que devem ser calculadas a largura e coordenada X da área salva.
Método que desenha um segmento de linha arbitrária:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Aqui estão as coordenadas e o tamanho da área salva são calculados a partir das coordenadas da linha desenhada.
Método que desenha uma linha 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; } //+------------------------------------------------------------------+
Aqui, o cálculo de coordenadas e tamanhos da área salva tem o mesmo o propósito, mas difere na execução dos métodos acima porque para uma linha quebrada (como para muitas outras formas) as coordenadas são transmitidas não por variáveis, mas em matrizes - afinal, aqui não podemos saber de antemão quantos ziguezagues a linha terá e quantas coordenadas passarão nos argumentos do método. Por isso, antes de chamar o método, é necessário preencher duas matrizes com as respectivas coordenadas X e as coordenadas Y de cada ponto de ziguezague da linha.
No método, obtemos os valores máximo e mínimo dessas matrizes usando a função considerada anteriormente que retorna da matriz passada a ela o valor mínimo ou máximo escrito na matriz. Os valores obtidos são usados para calcular as coordenadas e tamanhos da área salva do fundo da forma.
Outros métodos para desenhar formas sem suavização (preste atenção aos cálculos de coordenadas e tamanhos da área salva):
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Consideremos os métodos para desenhar primitivas sombreadas sem suavização.
Método para pintar a á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 o método pinta uma área fechada arbitrária, o tamanho da área salva não é conhecido com antecedência. Por isso, aqui toda a forma deve ser preservada. Para isso, definimos coordenadas e dimensões como zero. Com esses valores, o método que salva a área retangular da imagem salva toda a imagem de fundo da forma na matriz.
O outros métodos são o desenho de primitivas sombreadas - seu cálculo de coordenadas e tamanhos da área salva coincide com o cálculo em métodos semelhantes para desenho de primitivas simples não suavizadas, métodos esses que discutimos acima. Vamos ver os métodos como eles são:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Métodos para desenhar primitivas com suavização.
Método que define um ponto usando o algoritmo de suavização 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; } //+------------------------------------------------------------------+
Aqui o cálculo de coordenadas e tamanhos da área salva difere desse cálculo no método de desenho de ponto sem suavização. Como o ponto suavizado pode estar localizado em três pixels adjacentes (9 no total, ou seja, 3 x 3 pixels), as dimensões da área salva devem ter três pixels de altura e três de largura. As coordenadas X e Y, respectivamente, devem estar um pixel à esquerda e acima das coordenadas do próprio ponto. Assim, o retângulo da área salva de delineia o ponto terá uma margem de um pixel em todos os lados a partir do ponto desenhado, caso o ponto desenhado seja desfocado pelo algoritmo de suavização e desenhado em mais de um pixel. Assim, vamos nos livrar da restauração incompleta do fundo apagado pelo ponto desenhado com suavização.
Método que desenha um segmento de uma linha arbitrária usando o algoritmo de suavização AntiAliasing:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O teste deste método mostrou que as bordas da linha desenhada não estão borradas, por isso aqui o cálculo do tamanho da área salva corresponde ao cálculo no método de desenho de linha sem suavização.
Método que desenha um segmento de uma linha arbitrária usando o algoritmo de suavização 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 o cálculo é semelhante ao método anterior pelo mesmo motivo.
Método que desenha um segmento de uma linha arbitrária de uma determinada espessura usando o algoritmo de suavização com pré-filtragem:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Neste método o cálculo da área salva já difere de todos os itens acima. Como uma linha com tal algoritmo de suavização tem uma largura especificada e certa aparência de extremidades, a largura da área salva também deve levar em consideração o tamanho (espessura) da linha e suas bordas (as bordas da linha podem ser arredondadas, de modo que o tamanho (comprimento) da linha aumenta em dois raios de arredondamento, ou seja, no valor especificado como largura da linha).
Método que desenha um segmento vertical de uma linha arbitrária de uma determinada espessura usando um algoritmo de suavização com pré-filtragem:
//+----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Aqui o cálculo é o mesmo descrito para o método anterior.
Outros métodos de desenho para suavização e outras primitivas:
//+-----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Aqui, para todos os métodos, os algoritmos para calcular a área de fundo armazenada são aproximadamente idênticos aos algoritmos de cálculo nos métodos acima.
Gostaria de esclarecer que nos métodos de desenho de elipses (DrawEllipseAAOnBG e DrawEllipseWuOnBG) se o tamanho do retângulo dentro do qual a elipse é desenhada for menor que três pixels, a forma não será desenhada. Por isso, aqui nos cálculos, vale a pena verificar se há um tamanho inferior a três pixels. Ainda não entendi como a elipse é desenhada ou se está embutida assim nos métodos da classe CCanvas. Espero descobrir isso no futuro.
Todas as classes de quadros de animação de objetos necessários para hoje estão criadas.
Agora precisamos criar uma classe na qual possamos armazenar os objetos de quadro de animação criados, acessá-los e gerenciá-los.
A classe conterá duas (por enquanto) listas para armazenar os objetos criados de quadros de animação (texto e retangular), terá métodos para criar novos objetos e gerenciá-los. Posteriormente, todos os objetos de animação pertencentes à mesma forma serão armazenados nesta classe. Assim, cada forma terá seu próprio conjunto de objetos de animação que podem ser criados dinamicamente e adicionados à lista de animações da forma.
Classe para as animações da forma
Na pasta \MQL5\Include\DoEasy\Objects\Graph\Animations\ criamos o novo arquivo Animations.mqh da classe CAnimations.
Ao arquivo da classe devem ser anexados os arquivos recém-criados de classes herdeiros do objeto-quadro de animação base; a classe em si deve ser herdada do objeto base da biblioteca padrão 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 { }
Na seção privada da classe, declaramos um ponteiro para um objeto-elemento gráfico a partir do qual serão criados os objetos de animação, duas listas para armazenar dois tipos de objetos-quadros de animação e métodos para retornar o sinalizador da presença do objeto especificado na lista e para retornar um ponteiro para um objeto-quadro de animação existente ou pré-criá-lo se não estiver na 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:
Consideraremos todos os métodos mais adiante.
Na seção pública da classe, declararemos métodos para criar e trabalhar com objetos em listas e métodos para desenhar primitivas com armazenamento e restauração do plano de fundo:
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 }; //+------------------------------------------------------------------+
Vamos considerar a implementação dos métodos declarados.
Construtor paramétrico:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CAnimations::CAnimations(CGCnvElement *element) { this.m_element=element; } //+------------------------------------------------------------------+
Aqui definimos o valor do ponteiro m_element para o objeto-elemento gráfico passado nos argumentos.
Método que retorna um ponteiro para o objeto-quadro de animação por tipo e ID:
//+------------------------------------------------------------------+ //| Return the animation frame objects by type and ID | //+------------------------------------------------------------------+ CFrame *CAnimations::GetFrame(const ENUM_ANIMATION_FRAME_TYPE frame_type,const int id) { //--- Declare the pointer to the animation frame object CFrame *frame=NULL; //--- Depending on the necessary object type, receive their number in the appropriate list int total= ( frame_type==ANIMATION_FRAME_TYPE_TEXT ? this.m_list_frames_text.Total() : frame_type==ANIMATION_FRAME_TYPE_QUAD ? this.m_list_frames_quad.Total() : 0 ); //--- Get the next object in the loop ... for(int i=0;i<total;i++) { //--- ... by the list corresponding to the animation frame type switch(frame_type) { case ANIMATION_FRAME_TYPE_TEXT : frame=this.m_list_frames_text.At(i); break; case ANIMATION_FRAME_TYPE_QUAD : frame=this.m_list_frames_quad.At(i); break; default: break; } //--- if failed to get the pointer, move on to the next one if(frame==NULL) continue; //--- If the object ID correspond to the required one, //--- return the pointer to the detected object if(frame.ID()==id) return frame; } //--- Nothing is found - return NULL return NULL; } //+------------------------------------------------------------------+
A lógica do método é totalmente descrita nos comentários do código e não precisa de explicações adicionais.
Método que retorna um sinalizador da presença na lista de um objeto-quadro de animação com o tipo e 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); } //+------------------------------------------------------------------+
O método retorna o resultado bool da chamada do método GetFrame() considerado acima. Se o método GetFrame() um resultado diferente de NULL (ou seja, o objeto pesquisado está na lista), o método retornará true, caso contrário, false.
Método que cria um novo objeto-quadro de animação 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; } //+------------------------------------------------------------------+
A lógica do método é totalmente descrita nos comentários do código.
Método que cria um novo objeto-quadro de animação retangular:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O método é idêntico ao anterior, também é totalmente comentado e não precisa de explicações adicionais.
Método que retorna um ponteiro ou cria um novo objeto-quadro de animação:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. Se precisarmos trabalhar com um quadro de animação, podemos criá-lo antecipadamente, obter um ponteiro para ele e controlar o objeto resultante. Mas se precisarmos criar objetos dinamicamente, esse método permitirá, na ausência de um objeto com o identificador especificado, primeiro criar um novo objeto e, em seguida, retornar um ponteiro para ele. Assim, podemos realizar a criação dinâmica de objetos "de acordo com a situação", imediatamente obter um indicador para ele e trabalhar com ele.
Métodos para trabalhar com objetos-quadros de animação.
Método que exibe texto no plano de fundo com armazenamento e restauração do plano de fundo:
//+--------------------------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Ao método são passados o identificador de objeto, os parâmetros do texto exibido (o próprio texto, coordenadas X e Y, ponto de ancoragem, cor e opacidade), o sinalizador da necessidade de criar um novo objeto com o identificador especificado se um objeto com tal identificador não estiver na lista e o sinalizador de redesenho do gráfico. Em seguida, obtemos um ponteiro para o objeto requerido (ou criamos um objeto se ele estiver ausente). Se o ponteiro não puder ser obtido, retornamos false.
Se o ponteiro for recebido, retorna o resultado do método TextOnBG() do objeto-quadro de animação de texto recebido.
Método que define a cor do ponto com as 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); } //+------------------------------------------------------------------+
A lógica do método é idêntica à lógica do método acima. Ao método são passados o identificador de objeto, as coordenadas X e Y da forma desenhada, sua cor e opacidade, o sinalizador da necessidade de criar um novo objeto com o identificador especificado se um objeto com tal identificador não estiver na lista e o sinalizador de gráfico redesenhado.
Em seguida, obtemos um ponteiro para o objeto requerido (ou criamos um objeto se ele estiver ausente). Se o ponteiro não puder ser obtido, retornamos false.
Se o ponteiro for recebido, retornamos o resultado do método SetPixelOnBG() do objeto-quadro de animação retangular resultante.
O resto dos métodos são para desenhar primitivas.
A lógica dos outros métodos para desenhar formas é idêntica à lógica dos métodos acima. Vamos apenas ver sua listagem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Assim, a classe criada de objetos-animações deve ser uma parte componente do objeto-forma. Dessa forma, cada forma terá seus próprios métodos de desenho - exclusivos para cada objeto-forma.
Abrimos o arquivo da classe do objeto-forma \MQL5\Include\DoEasy\Objects\Graph\Form.mqh e incluímos as modificações necessárias.
Vamos anexar a ele o arquivo da classe dos objetos-animações:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Removemos da lista a classe do objeto-copiador de pixels (nós o movemos para outro arquivo):
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GCnvElement.mqh" #include "ShadowObj.mqh" #include "Animations\Animations.mqh" //+------------------------------------------------------------------+ //| Pixel copier class | //+------------------------------------------------------------------+ class CPixelCopier : public CObject { private: ... } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+
Na seção privada da classe em vez de uma lista de pixels de cópia
CArrayObj m_list_pc_obj; // List of pixel copier objects
declaramos um ponteiro para um objeto da classe de animação:
//+------------------------------------------------------------------+ //| 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:
Também da seção privada removemos a declaração do método IsPresentPC() já desnecessário e da lista de códigos excluímos sua implementação:
//--- 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:
Da seção pública da classe removemos o método que não é mais necessário:
//--- 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; }
e adicionamos novos métodos que retornam ponteiros para o objeto de animação e para listas de texto e quadros de animação retangulares:
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
Removemos a declaração do método para criar um novo objeto-copiador de pixels:
//--- 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
Também excluiremos a implementação desse método, escrita fora do corpo da classe.
Na seção pública da classe, no bloco de métodos para trabalhar com pixels de imagem, vamos escrever novos métodos para criar objetos-quadros de animação, retornando ponteiros para objetos criados e métodos de desenho com salvamento e restauração do fundo:
//+------------------------------------------------------------------+ //| 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 os métodos retornam o resultado dos métodos correspondentes do objeto de animação CAnimations que criamos acima.
No destruidor da classe removemos o objeto de animação:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CForm::~CForm() { if(this.m_shadow_obj!=NULL) delete this.m_shadow_obj; if(this.m_animations!=NULL) delete this.m_animations; } //+------------------------------------------------------------------+
No método de inicialização criamos um novo objeto de animação:
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+
Agora, ao criar um novo objeto-forma, para ele será criado automaticamente um objeto de animação, no qual podemos adicionar dinamicamente novos quadros de animações ou criar formas predefinidas ao criar formas. Ao concluir o trabalho, o objeto de animação será destruído para evitar vazamentos de memória.
Estamos prontos para testar as classes criadas.
Vejamos que e como vamos testar. Temos um Expert Advisor de teste que desenha quatro formas. Ao clicar na quarta foma, isto é, na inferior, é movido o rótulo "H-Gradient" que indica o método de preenchimento (horizontal). Agora vamos criar e mover este rótulo como um objeto-quadro de animação de texto. Usaremos a terceira forma - com preenchimento vertical - para exibir várias formas. Mas antes de desenhar a forma, renderizaremos o texto como um quadro de texto de animação e, em seguida, desenharemos formas em cima dele. Geraremos a alternância dos tipos de figuras desenhadas pressionando as teclas do teclado, para o texto exibir a figura selecionada. Cada clique do mouse na forma mudará as coordenadas dos pontos da forma desenhada.
Teste
Para o teste, vamos pegar o Expert Advisor do artigo anterior e o salvamos na nova pasta \MQL5\Experts\TestDoEasy\Part79\ com o novo nome TestDoEasyPart79.mq5.
Dentro das variáveis globais vamos inserir substituições de macro para especificar as coordenadas iniciais das figuras desenhadas e vamos declarar variáveis para armazenar as coordenadas de diferentes pontos das figuras desenhadas (usaremos cinco pontos no total) e os valores de sua mudança:
//+------------------------------------------------------------------+ //| 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; //+------------------------------------------------------------------+
No manipulador OnInit(), nos blocos para criar as duas últimas formas, agora vamos criar o texto usando objetos-animações 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
Nós criamos esses objetos de texto aqui de antemão. Vamos criar as formas desenhadas dinamicamente - clicando com o mouse na área da forma.
Para processamento dos pressionamentos de tecla, ao manipulador OnChartEvent() adicionamos esse bloco 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 mudar ligeiramente o código do bloco para processamento de cliques nas formas - agora o texto será exibido usando objetos-animações de texto da forma clicada:
//--- 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; } } } }
O clique na terceira forma (com identificador 2) será processado na função FigureProcessing(), ao clicar na quarta forma (com identificador 3), como antes, determinaremos o ângulo da âncora do texto em função do valor da variável n e exibiremos o texto. Mas o texto agora será exibido usando a classe de objeto do quadro de animação de texto.
Função auxiliar que retorna o tipo de forma com base na tecla pressionada:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
A função na qual processamos o clique do objeto-forma é bastante grande. Na verdade, é simples, mas volumosa, porque só contém um operador switch no qual é processado cada botão pressionado, que corresponde à forma desenhada. O processamento é simples, definimos as coordenadas iniciais, verificamos se elas vão além da faixa de valores permitida, desenhamos uma figura com esses parâmetros e aumentamos os deslocamentos dos valores dos parâmetros. Ao clicar novamente na forma, a forma anterior será apagada pelo fundo restaurado e um novo será desenhado nas novas coordenadas de pontos.
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Espero que o código dessa função seja fácil de entender e não suscite dúvidas. Em qualquer caso, tudo pode ser discutido nos comentários ao artigo.
Para um teste simples, não fiz o processamento geral de parâmetros semelhantes. É mais fácil e rápido escrever tudo "na testa" do operador switch. Não é irrelevante que o código seja repetitivo, é mais importante verificar a integridade das classes criadas.
Vamos compilar o Expert Advisor e executá-lo no gráfico. Após a inicialização, pressionamos vários botões do teclado para garantir que o modo de desenho muda e é exibido no rótulo da terceira forma. Se clicarmos na quarta forma, o texto se moverá da mesma maneira que no Expert Advisor anterior do artigo anterior. Só agora esse texto é exibido por meio da classe do objeto-quadro de texto de animação.
Se, depois de selecionar o modo de desenho desejado, começarmos a clicar na terceira forma (na qual é exibido um rótulo mostrando o modo de desenho), então sobre o rótulo e a própria forma a figura selecionada será desenhada com os valores das coordenadas de pontos mudando sempre.
Nem todos os métodos de desenho funcionaram bem. Por exemplo, o método FillPolygon() da classe CCanvas, com os parâmetros iniciais definidos em nossa função para processamento de cliques de objeto, simplesmente cai num loop infinito, enquanto o método EllipseAA() gera o erro de divisão por zero. Este método tem pontos potenciais onde isso pode acontecer:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Eu levantei essa questão na discussão principal. Embora não haja resposta dos desenvolvedores, tentaremos solucionar esses erros em artigos futuros.
Com base no exposto acima, no manipulador de cliques do mouse no objeto-forma, alguns métodos de desenho não são chamados, mas a função simplesmente os retorna. Em qualquer caso, isso será corrigido no futuro. Mas agora vemos que o fundo sob as formas desenhadas foi restaurado e tudo é exibido como pretendido. Há um problema com a alternância de modos de desenho - depois de mudar, o fundo não é restaurado. Mas isso não se deve mais a algum erro, mas sim à falta de um método para deletar a imagem com a restauração do fundo. Faremos isso em artigos futuros, mas aqui - para teste e exemplo - simplesmente mudamos o período gráfico para restaurar completamente o plano de fundo antes de exibir outra figura na forma.
O que vem agora?
No próximo artigo, daremos continuidade ao desenvolvimento das classes de animação.
Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.
*Artigos desta série:
Gráficos na biblioteca DoEasy (Parte 73): objeto-forma de um elemento gráfico
Gráficos na biblioteca DoEasy (Parte 74): elemento gráfico básico baseado na classe CCanvas
Gráficos na biblioteca DoEasy (Parte 75): métodos para trabalhar com primitivas e texto num elemento gráfico básico
Gráficos na biblioteca DoEasy (Parte 76): objeto Forma e temas de cores predefinidos
Gráficos na biblioteca DoEasy (Parte 77): classe do objeto Sombra
Gráficos na Biblioteca DoEasy (Parte 78): princípios de animação dentro da biblioteca. Corte de imagens
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9652
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso