English Русский 中文 Español Deutsch 日本語
preview
Usando a classe CCanvas em aplicativos MQL

Usando a classe CCanvas em aplicativos MQL

MetaTrader 5Exemplos | 21 junho 2022, 09:48
496 2
Mihail Matkovskij
Mihail Matkovskij

Este artigo aborda a estrutura da classe CCanvas, bem como seu uso em aplicações MQL. Para consolidar os conhecimentos adquiridos, veremos exemplos práticos.


Gráficos em aplicativos

Para trabalhar com gráficos em qualquer aplicativo, precisamos acessar os dados de pixeis de tela. Se esse acesso existir, poderemos alterar as cores dos pixels conforme desejarmos, criar todos os tipos de elementos de interface do usuário, campos de entrada, botões, painéis, janelas e outros elementos ou exibir imagens de arquivos, receber dados de pixels e processá-los. Normalmente o mecanismo para tal é gerado em uma classe com a palavra Canvas em seu nome. Em linguagens de programação semelhantes a C, geralmente é CCanvas ou Canvas, dependendo do estilo de codificação. Assim, chegamos à conclusão de que para acessar os gráficos, qualquer aplicativo precisa de um Canvas.


Canvas em aplicativos MQL

Em MQL, essa ferramenta é representada como a classe CCanvas, que contém uma matriz de pixels, métodos para modificar tal matriz e um mecanismo para exibi-la no gráfico do terminal. Um dos objetos gráficos, OBJ_BITMAP ou OBJ_BITMAP_LABEL é usado como mecanismo de saída. O objeto gráfico utilizado recebe dados a partir do recurso gráfico, que por sua vez é carregado com dados de uma matriz de pixels.

Com a classe CCanvas, podemos implementar todos os recursos da interface do usuário ou qualquer ideia que um programador possa ter. Além das interfaces de usuário, com o CCanvas, podemos desenhar buffers de indicadores, barras de preços Open, Close, Low e High no gráfico e muito mais... Mas este não é um tema para ser discutido neste artigo. Este tópico será abordado com mais detalhes mais para frente.


Analisando CCanvas

Então, chegamos à parte mais interessante, a análise da classe CCanvas. O desenvolvimento posterior desta ferramenta dependerá de se foi compreendida corretamente. Vejamos a estrutura de CCanvas no seguinte diagrama.

Delving into CCanvas

Após analisar esse esquema, vemos que a classe CCanvas é dividida em duas seções, que são dados e métodos.

Vejamos os dados:

  • Dados de pixels: eles são a matriz de pixels m_pixels que armazena os pixels processados por métodos de desenho, bem como os dados m_width e m_height, isto é, a largura e a altura da imagem, respectivamente. Os parâmetros m_width e m_height são muito importantes para lidar com a matriz m_pixels dos métodos de desenho e para passar m_pixels para o recurso gráfico no método Update.
  • Dados de fonte para exibição de texto: consistem nos campos m_fontname, m_fontsize, m_fontflags e m_fontangle - nome da fonte, tamanho da fonte, atributos da fonte e ângulo do texto, respectivamente. Esses dados são necessários para definir e obter as propriedades da fonte (usando os métodos de propriedades de fonte de leitura/gravação) antes de chamar o método TextOut.
  • Dados para desenho de linha: consiste em dois campos, isto é, o estilo de linha, m_style, e o índice de bits atual m_style_idx no modelo de estilo de linha. Eles são necessários para que os métodos de desenho funcionem.
  • Dados padrão: neste caso existe apenas um campo m_default_colors, que é uma matriz de cores padrão. Ela não é usada nos métodos da classe CCanvas em si. Mas pode ser útil para outras funções ou métodos de classes derivadas do CCanvas como seletor de cores.
  • Dados para interação com o gráfico: são os seguintes dados m_chart_id, m_objname, m_objtype, m_rcname e m_format , que são o ID do objeto do gráfico, nome do objeto do gráfico, tipo do objeto do gráfico, nome do recurso gráfico e formato do pixel, respectivamente. São projetados para trabalhar com os métodos. Destroy, Update, Resize. Em particular, o campo m_format é necessário para que o método TextOut funcione.


Vejamos os métodos:

  • Métodos para criar/anexar/remover arquivos: categoria que contém vários métodos que trabalham com o objeto gráfico. Existem métodos para criar o objeto gráfico, eles são CreateBitmap e CreateBitmapLabel. São projetados para criar um objeto e um recurso gráfico associado a ele, que, por sua vez, gera uma imagem. Para o método de anexo Attach vemos que se o gráfico tiver um objeto gráfico OBJ_BITMAP_LABEL com ou sem recurso gráfico anexado, o CCanvas trabalhará com ele da mesma forma que com o objeto gráfico a ser criado. Basta anexá-lo usando o método Attach correspondente. O método de remoção é apresentado como Destroy. Ele exclui o objeto gráfico, libera o buffer da matriz de pixels m_pixels e exclui o recurso gráfico associado ao objeto gráfico. Por esse motivo, para que o CCanvas funcione corretamente, devemos sempre chamar o método Destroy quando ele terminar, pois o CCanvas não exclui automaticamente seu objeto gráfico e recurso gráfico.
  • Métodos para carregar imagens a partir de um arquivo: método estático LoadBitmap, que é capaz de carregar um arquivo *.bmp em qualquer matriz uint passada a ele como parâmetro, salva as dimensões da imagem nas variáveis width e height, que também são passadas para este método por endereços como seus parâmetros. O método LoadFromFile carrega um arquivo *.bmp na matriz m_pixels, configura as dimensões da imagem para m_width e m_height. O formato de pixel m_format deve ser igual a COLOR_FORMAT_ARGB_RAW.
  • Métodos para ler as propriedades do objeto gráfico: eles são ChartObjectName, ResourceName, Width e Height , e retornam o nome do objeto gráfico, o nome do recurso gráfico, a largura e a altura da imagem, respectivamente. Eles permitem que o usuário leia apenas alguns dos dados de interação com o gráfico, como m_objname, m_rcname, bem como dados de imagem m_width e m_height.
  • Métodos para ler/escrever as propriedades da fonte para o texto exibido: vejamos que os métodos de registro FontNameSet, FontSizeSet, FontFlagsSet e FontAngleSet definem o nome da fonte, o tamanho da fonte, os atributos da fonte e o ângulo do texto exibido, respectivamente. Agora reparemos que os métodos de leitura FontSizeGet, FontFlagsGet e FontAngleGet retornam o tamanho da fonte, os atributos da fonte e o ângulo do texto exibido, respectivamente. Existem também métodos para obter/configurar propriedades de fonte que retornam/configuram todas as propriedades de fonte de uma só vez. O método para definir propriedades FontSet define o nome da fonte, tamanho da fonte, atributos da fonte e o ângulo do texto exibido. o método para obter propriedades FontGet retorna o nome da fonte, o tamanho da fonte, os atributos da fonte e o ângulo do texto exibido. 
  • Métodos de leitura/gravação de estilo de desenho de linha: o método LineStyleGet é usado para leitura, LineStyleSet serve para registro. O estilo de linha é necessário para os métodos de desenho das primitivas gráficas LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA, EllipseAA, LineWu, PolylineWu, PolygonWu, TriangleWu, CircleWu, EllipseWu, LineThickVertical, LineThickHorizontal, LineThick, PolylineThick, PolygonThick.
  • Métodos para desenhar em uma matriz de pixels: a classe CCanvas tem alguns métodos para desenhar primitivas gráficas usando vários algoritmos, o que dá ao usuário a capacidade de criar gráficos complexos usando métodos avançados de antisserrilhamento, como antisserrilhamento, algoritmo de Wu e curvas de Bezier. Vejamos esses métodos. Primitivas simples sem antisserrilhamento como LineVertical, LineHorizontal, Line, PolylinePolygon, Rectangle, Triangle, Circle, Ellipse, Arc e Pie. Os métodos desenham as primitivas linha vertical, linha horizontal, linha arbitrária, polilinha, polígono, retângulo, triângulo, círculo, elipse, arco e setor preenchido de uma elipse, respectivamente. As primitivas sombreadas incluem FillRectangle, FillTriangle, FillPolygon, FillCircle, FillEllipse e Fill. Elas desenham retângulo, triângulo, polígono, círculo, elipse e preenchimento de área, respectivamente. Os métodos para desenhar primitivas com antisserrilhamento (AA) são PixelSetAA, LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA e EllipseAA. Eles geram, preenchem o pixel e exibem primitivas como uma linha arbitrária, polilinha, polígono, triângulo, círculo e elipse, respectivamente. Os métodos para desenhar primitivas como algoritmo deWu são LineWu, PolylineWu, PolygonWu, TriangleWu, CircleWu e EllipseWu. Eles desenham primitivas como uma linha arbitrária, uma polilinha, um polígono, um triângulo, um círculo e uma elipse, respectivamente. Os métodos para desenhar primitivas com antisserrilhamento pré-filtrado e espessura de linha ajustável são LineThickVertical, LineThickHorizontal, LineThick, PolylineThick e PolygonThick. Eles desenham as primitivas linha vertical, linha horizontal, linha de forma livre, polilinha e polígono, respectivamente. Os métodos para desenhar primitivas com suavização de serrilhado por meio dos métodos Bezier são PolylineSmooth e PolygonSmooth. Eles desenham primitivas como a linha suavizada e o polígono suavizado, respectivamente. Além dos métodos acima, esta categoria também inclui o método TextOut para exibição de texto, pois também altera os valores de cores na matriz de pixels, embora no código-fonte da classe CCanvas se refira a métodos para trabalhar com texto.
  • Métodos para transferir uma imagem para exibição no gráfico: esta categoria inclui o objeto método Update, que passa a matriz de pixels m_pixels para o recurso gráfico associado ao objeto gráfico que exibe uma imagem nele. Lembre que a matriz de pixels m_pixels é modificada usando os métodos de desenho da matriz de pixels descritos acima, assim como o método Resize, que redimensiona o array m_pixels (dimensões da imagem) e também o passa para o recurso gráfico.
  • Serviços: existem dois desses métodos no CCanvas, isto é, GetDefaultColor, que retorna as cores predefinidas, e TransparentLevelSet, que altera a transparência da imagem mudando os valores alfa na matriz m_pixels.
  • Outras configurações: existe apenas um método FilterFunction para configuração de filtro antisserrilhamento, que define o filtro para todos os métodos de desenho cujos nomes possuem os caracteres AA.

A classe CCanvas possui campos e métodos no escopo privado, que não consideraremos neste artigo, pois são internos e não podem ser usados nos métodos sobrescritos das classes descendentes CCanvas. Porém eles podem ser estudados abrindo o código fonte do módulo Canvas.mqh no MetaEditor.


Sequência de ações ao usar o CCanvas em aplicativos MQL

Com base no que analisamos acima, podemos distinguir uma sequência geral de passos para manusear a classe CCanvas quando usada em qualquer aplicativo MQL. Vejamos quais os passos a realizar para que a imagem apareça no gráfico.

  • Criar/anexar um objeto gráfico (OBJ_BITMAP ou OBJ_BITMAP_LABEL) ou anexar a um OBJ_BITMAP_LABEL existente
  • Definir parâmetros de fonte e de estilo de desenho primitivas
  • Desenhar na matriz m_pixels usando os métodos apropriados
  • Atualizar o recurso do objeto gráfico (OBJ_BITMAP ou OBJ_BITMAP_LABEL)

Como resultado, no gráfico veremos um objeto gráfico com construções gráficas ou texto. Vamos dar uma olhada em todas esses passos.

Criando um objeto gráfico e anexando-o a um objeto gráfico existente

Para que um aplicativo MQL possa exibir gráficos, um objeto deve ser criado com base na classe CCanvas ou com base na classe herdeira CCanvas. Depois de criar o objetoCCanvas, podemos começar a criar o objeto gráfico OBJ_BITMAP ou OBJ_BITMAP_LABEL, ou anexar um OBJ_BITMAP_LABEL existente ao objeto CCanvas criado.

Para criar um objeto gráfico , o CCanvas possui dois métodos CreateBitmap e CreateBitmapLabel, cada um com sua própria sobrecarga para facilitar o uso.

bool  CreateBitmap(const long chart_id, const int subwin, const string name, const datetime time, const double price, const int width, const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmap(const string name, const datetime time, const double price, const int width, const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmapLabel(const long chart_id, const int subwin, const string name, const int x, const int y, const int width, const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);


O método CreateBitmap cria um bitmap (tipo de objeto OBJ_BITMAP), cujas coordenadas são especificadas como hora e preço e contém os seguintes parâmetros:

  • chart_id – ID do gráfico (0, gráfico atual)
  • window – número da subjanela do gráfico (0, janela principal)
  • name – nome do objeto gráfico criado no gráfico
  • time – coordenada de tempo do objeto gráfico no gráfico
  • price – coordenada de preço do objeto gráfico no gráfico
  • width - largura do objeto gráfico no gráfico
  • height - altura do objeto gráfico no gráfico

Outra variação do método CreateBitmap é um método sobrecarregado que chama CreateBitmap com chart_id e window definidos como 0 (correspondente ao gráfico atual e à janela principal).

//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmap(const string name,const datetime time,const double price,
                           const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmap(0,0,name,time,price,width,height,clrfmt));
  }

O método CreateBitmapLabel tem todos os mesmos parâmetros que CreateBitmap, exceto time e price. Em vez disso, vemos x e y.

Parâmetros:

  • chart_id – ID do gráfico (0, gráfico atual)
  • window – número da subjanela do gráfico (0, janela principal)
  • name – nome do objeto gráfico criado no gráfico
  • x – coordenar ao longo do eixo X do objeto gráfico no gráfico
  • y – coordenada Y do objeto gráfico no gráfico
  • width– largura da imagem do objeto gráfico criado
  • height – altura da imagem do objeto gráfico criado
  • clrfmt – formato de cor dos pixel da imagem do objeto gráfico criado

Outra variação do método CreateBitmapLabel é um método sobrecarregado que chama CreateBitmapLabel com chart_id e window definidos como 0 (como é o caso de CreateBitmap).

//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y,
                                const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt));
  }

Se houver um objeto OBJ_BITMAP_LABEL no gráfico, ele poderá ser anexado ao CCanvas usando o método Attach. Como resultado, o objeto gráfico irá interagir com o objeto CCanvas da mesma forma que com o objeto gráfico criado usando o método CreateBitmap ou CreateBitmapLabel.

virtual bool  Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

virtual bool  Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);


Este método anexa o CCanvas a um objeto gráfico OBJ_BITMAP_LABEL já existente.

Parâmetros:

  • chart_id – ID do gráfico (0, gráfico atual)
  • objname – nome do objeto gráfico anexado
  • width – largura da imagem do objeto gráfico anexado
  • height – altura da imagem do objeto gráfico anexado
  • clrfmt – formato de cor dos pixels da imagem do objeto gráfico anexado

A primeira variante do método Attach assume que o objeto gráfico não possui um recurso gráfico e o cria de forma independente com base nos parâmetros objname, width, height e clrfmt, preenchendo todos os dados necessários para o trabalho posterior com o objeto gráfico anexado .

A segunda variante assume a existência de um recurso gráfico e simplesmente lê os dados de pixel da imagem na matriz m_pixels, preenchendo também todos os dados necessários para continuar a trabalhar com o objeto gráfico anexado.

Métodos para definir e obter parâmetros de fonte e métodos para definir e obter estilo de desenho de linha

Depois que o objeto gráfico é criado usando o método CreateBitmap ou CreateBitmapLabel ou anexado usando o método Attach, para obter a imagem desejada, devemos definir os parâmetros da fonte e o estilo de desenho das primitivas. Os parâmetros de fonte são definidas usando os métodos a seguir.

Métodos simples para definir as propriedades da fonte:

bool FontNameSet(string name);  // Set the font name

bool FontSizeSet(int size);     // Set the font size

bool FontFlagsSet(uint flags);  // Set the font attributes

bool FontAngleSet(uint angle);  // Set the font slope angle


Métodos simples para ler as propriedades da fonte:

string FontNameGet(void) const;   // Return the font name

int    FontSizeGet(void) const;   // Return the font size

uint   FontFlagsGet(void) const;  // Return the font attributes

uint   FontAngleGet(void) const;  // Return the font slope angle


Método para definir todas as propriedades da fonte

bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); // Set the font properties

Parâmetros:

  • name - nome da fonte
  • size - tamanho da fonte
  • flags - atributos de fonte
  • angle - ângulo da fonte

Como você pode ver na listagem, este método define o nome da fonte, tamanho da fonte, atributos da fonte e ângulo do texto de acordo com os valores das variáveis name, size, flags e angle, respectivamente, que são passados para ele como parâmetros.

Método para obter todas as propriedades da fonte

void FontGet(string &name,int &size,uint &flags,uint &angle); // Get font properties

Parâmetros:

  • name - nome da fonte
  • size - tamanho da fonte
  • flags - atributos de fonte
  • angle - ângulo da fonte

Como você pode ver na listagem, esse método grava o nome da fonte, o tamanho da fonte, os atributos da fonte e o ângulo do texto nas variáveis name, size, flags e angle apropriadas, que são passadas a ele como parâmetros de endereço .

Métodos para ler e escrever o estilo de linha de construções gráficas:

uint LineStyleGet(void) const;       // Return the specified line drawing style

void LineStyleSet(const uint style); // Set the line drawing style


    Parâmetros:

    • style - estilo de desenho de linha

    Métodos de desenho, bem como método de exibição de texto 

    Vamos começar examinando o método de exibição de texto, já que existe apenas um. Embora existam muitos métodos para desenhar primitivas gráficas no CCanvas.

    Exibição de texto

    void TextOut(int x,int y,string text,const uint clr,uint alignment=0); // Display text to the m_pixels array

    Parâmetros:

    • x - coordenar ao longo do eixo X do texto exibido
    • y - coordenar ao longo do eixo Y do texto exibido
    • text - texto exibido
    • clr - cor do texto exibido
    • alignment - como o texto exibido será ancorado

    Como você pode ver na listagem, o método gera o texto text, de acordo com as coordenadas x, y, com a cor fornecida clr e o alignment do método de ancoragem de texto.

    Alteração de pixels

    No CCanvas, os pixels localizados na matriz m_pixels podem ser alterados ou seus valores podem ser obtidos de acordo com as coordenadas especificadas.

    uint PixelGet(const int x,const int y) const;          // Return the pixel color value according to x and y coordinates
    
    void PixelSet(const int x,const int y,const uint clr); // Change the pixel color value according to x and y coordinates
    
    
    

    Parâmetros:

    • x - coordenada x do pixel
    • y - coordenada Y do pixel
    • clr - cor do pixel

    Desenho de primitivas gráficas

    Como existem muitos métodos para desenhar primitivas no CCanvas, neste artigo vamos analisar apenas os principais. Todos os outros métodos da classe CCanvas podem ser encontrados em detalhes na documentação.

    Linha vertical

    void LineVertical(int x,int y1,int y2,const uint clr); // Draw a vertical line according to specified coordinates and color

    Parâmetros:

    • x - coordenada X da linha
    • y1 - coordenada do primeiro ponto ao longo do eixo Y
    • y2 - coordenada do segundo ponto ao longo do eixo Y
    • clr - cor da linha

    Linha horizontal

    void LineHorizontal(int x1,int x2,int y,const uint clr); // Draw a horizontal line according to specified coordinates and color

    Parâmetros:

    • x1 - coordenada do primeiro ponto ao longo do eixo X
    • x2 - coordenada do segundo ponto ao longo do eixo X
    • y - coordenada da linha ao longo do eixo Y
    • clr - cor da linha

    Linha livre

    void Line(int x1,int y1,int x2,int y2,const uint clr); // Draw a freehand line according to specified coordinates and color

    Parâmetros:

    • x1 - coordenada do primeiro ponto ao longo do eixo X
    • y1 - coordenada do primeiro ponto ao longo do eixo Y 
    • x2 - coordenada do segundo ponto ao longo do eixo X 
    • y2 - coordenada do segundo ponto ao longo do eixo Y
    • clr - cor da linha

    linha quebrada

    void Polyline(int &x[],int &y[],const uint clr); // Draw a polyline according to specified coordinates and color

    Parâmetros:

    • x - matriz de coordenadas de ponto ao longo do eixo X
    • y - matriz de coordenadas de pontos ao longo do eixo Y
    • clr - cor da linha

    Polígono

    void Polygon(int &x[],int &y[],const uint clr); // Draw a polygon according to specified coordinates and color

    Parâmetros:

    • x - matriz de coordenadas de ponto ao longo do eixo X
    • y - variedade coordenadas de pontos ao longo do eixo Y
    • clr - cor do polígono

    Retângulo

    void Rectangle(int x1,int y1,int x2,int y2,const uint clr); // Draw a rectangle according to specified coordinates and color

    Parâmetros:

    • x1 - coordenada do primeiro ponto ao longo do eixo X
    • y1 - coordenada do primeiro ponto ao longo do eixo Y
    • x2 - coordenada do segundo ponto ao longo do eixo X
    • y2 - coordenada do segundo ponto ao longo do eixo Y
    • clr - cor do retângulo 

    Triângulo

    void Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); // Draw a triangle according to specified coordinates and color

    Parâmetros:

    • x1 - coordenada do primeiro ponto ao longo do eixo X
    • ano 1 - coordenada do primeiro ponto ao longo do eixo Y
    • x2 - coordenada do segundo ponto ao longo do eixo X
    • ano 2 - coordenada do segundo ponto ao longo do eixo Y
    • x3- coordenada do terceiro ponto ao longo do eixo X
    • y3- coordenada do terceiro ponto ao longo do eixo y
    • clr - cor do triângulo

    Círculo

    void Circle(int x,int y,int r,const uint clr); // Draw a circle according to specified coordinates, radius and color

    Parâmetros:

    • x - coordenada X
    • y - coordenada Y
    • r - raio do círculo
    • clr - cor do círculo

    Elipse

    void Ellipse(int x1,int y1,int x2,int y2,const uint clr); // Draw an ellipse according to specified coordinates and color

    Parâmetros:

    • x1 - coordenada do primeiro ponto ao longo do eixo X
    • ano 1 - coordenada do primeiro ponto ao longo do eixo Y
    • x2 - coordenada do segundo ponto ao longo do eixo X
    • ano 2 - coordenada do segundo ponto ao longo do eixo Y
    • clr - cor da elipse

    As descrições de outros métodos, como Arc e Pie, são muito extensos no escopo deste artigo. Por isso, recomendo ver as descrições desses métodos na documentação nos links acima.

    Os métodos considerados geram primitivas simples com uma espessura de linha de 1 pixel e com o estilo de linha mais comum STYLE_SOLID. Se você estiver satisfeito com esses gráficos simples, poderá usá-los.

    Mas se você precisar de primitivas com um estilo de linha diferente de STYLE_SOLID, recomendo escolher um dos seguintes métodos.

    //--- Methods for drawing primitives with smoothing using antialiasing
        
    void PixelSetAA(const double x,const double y,const uint clr);
        
    void LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
        
    void PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
        
    void PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
        
    void TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX);
        
    void CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
        
    void EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX);
        
    //--- Methods for drawing primitives with smoothing using Wu's algorithm
        
    void LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX);
        
    void PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
        
    void PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
        
    void TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX);
        
    void CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
        
    void EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
    

    Todos esses métodos são semelhantes aos métodos simples vistos acima, mas possuem um parâmetro adicional (style) que pode ser selecionado na enumeração ENUM_LINE_STYLE). Ele está destacado em amarelo na listagem. Se o parâmetro style não for definido (igual a UINT_MAX), o método chamado usará o valor m_style, que é definido usando o método LineStyleSet e que pode ser obtido usando o método LineStyleGet.

    Como os leitores atentos devem ter notado, os métodos considerados têm a capacidade de definir o estilo das linhas, mas sem a possibilidade de alterar sua espessura. Por isso, existem os seguintes métodos para desenhar primitivas com definição de espessura de linha.

    //--- Methods for drawing primitives with preliminarily set antialiasing filter
    
    void LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    

    Nesses métodos, como nos métodos discutidos acima, é possível definir o estilo de linha. Também foram adicionados novos parâmetros como size, que define a espessura da linha, bem como end_style, que estabelece o estilo de finalização de linha, que pode ser selecionado na enumeraçãoENUM_LINE_END.

    Também noCCanvas existem métodos para desenhar primitivas suavizadas por meio do método Bezier. Em seguida, para melhorar a qualidade da imagem final, as primitivas resultantes são processadas usando um algoritmo de antisserrilhamento raster. Assim, ao invés de linhas retas, como nos métodos anteriores, as primitivas são compostas por curvas de Bezier. Vejamos esses métodos.

    //--- Methods for drawing a smoothed polyline and smoothed polygon
    
    void PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,
                        ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                        double tension=0.5,double step=10);
    
    void PolygonSmooth(int &x[],int &y[],const uint clr,const int size,
                       ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                       double tension=0.5,double step=10);
    

    Aqui, além das já familiares matrizes de coordenadas dos pontos x e y da primitiva, bem como a cor clr e a espessura da linha size, o estilo de linha style e o estilo de terminação de linha end_style, existem dois parâmetros adicionais tension, que é o valor do parâmetro de suavização, e step, que é o passo de aproximação.

    Além dos métodos discutidos acima para desenhar primitivas que consistem em linhas, o CCanvas possui métodos para desenhar primitivas preenchidas.

    //--- Methods of drawing filled primitives
    
    void FillRectangle(int x1,int y1,int x2,int y2,const uint clr);
    
    void FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr);
    
    void FillPolygon(int &x[],int &y[],const uint clr);
    
    void FillCircle(int x,int y,int r,const uint clr);
    
    void FillEllipse(int x1,int y1,int x2,int y2,const uint clr);
    
    void Fill(int x,int y,const uint clr);
    
    void Fill(int x,int y,const uint clr,const uint threshould);
    

    Esses métodos são simples e não possuem algoritmos antisserrilhamento. A atribuição de parâmetros é semelhante aos parâmetros dos métodos anteriores considerados que criam primitivas semelhantes a partir de linhas.


    Formato de pixel e componentes de cor

    Proponho retornar aos métodos discutidos acima e prestar atenção ao formato de pixel nos métodos CreateBitmap e CreateBitmapLabel, pelos quais o parâmetro clrfmt é responsável. Este parâmetro define o formato de pixel para o recurso gráfico criado, que posteriormente é associado ao objeto gráfico que exibe a imagem nele. A forma como o terminal processa a imagem durante sua exibição no gráfico dependerá do formato de pixel que definimos para o recurso gráfico ao criar a tela usando os métodos CreateBitmap ou CreateBitmapLabel. Para entender quais métodos de processamento de imagem existem, vamos nos voltar para a enumeração ENUM_COLOR_FORMAT. Aqui podemos escolher um dos três valores constantes.

    ENUM_COLOR_FORMAT

    • COLOR_FORMAT_XRGB_NOALPHA - formatoXRGB (canal alfa é ignorado)
    • COLOR_FORMAT_ARGB_RAW - formato ARGB "raw" (componentes de cor não são processados pelo terminal)
    • COLOR_FORMAT_ARGB_NORMALIZE - formato ARGB (componentes de cor são processados pelo terminal)

    Agora vejamos a ordem em que os componentes de cor são representados nesses formatos.

    Pixel format and color components

    No diagrama apresentado, vemos a localização dos componentes de cor nos bytes das células da matriz de pixels m_pixels, onde R é o canal vermelho, G é o canal verde, B é o canal azul, A é o canal alfa, X é o byte não utilizado. Tipo de dados de pixel uint, 4 bytes. Um byte por canal. Um byte pode armazenar números de 0 a 255. Ao alterar os valores dos bytes, definimos a cor do pixel. Assim, por exemplo, se escrevermos o valor 255 no canal R e escrevermos valores 0 nos canais G e B, obteremos a cor vermelha. Se escrevermos o valor 255 no canal G e definirmos o resto como 0, obtemos a cor verde. E se escrevermos o valor 255 no canal B e escrevermos valores 0 no restante, obteremos a cor azul. Ao definir os valores dos canais RGB em diferentes proporções, podemos obter absolutamente qualquer cor. Ao definir o valor do canal alfa de 0 (absolutamente invisível, transparente) a 255 (absolutamente visível, opaco), podemos ajustar a opacidade do pixel, criando o efeito de sobreposição dos pixels de uma imagem em uma imagem que já foi mapeada. Podemos fazer isso corretamente usando o formato COLOR_FORMAT_ARGB_NORMALIZE. Agora vamos direto para as cores e considerar a paleta de cores.

    Color palette

    Aqui você pode ver claramente como a mudança na proporção dosníveis de cada canal afeta a cor final. As cores estão no formato RGB e HEX. Se já consideramos o formatoRGB, oHEX precisa de alguma explicação, especialmente para programadores iniciantes que desejam dominar o CCanvas. Analisemos o sistema de numeração hexadecimal, que contém números de 0 a 15, e o comparemos com o sistema de numeração decimal, onde os números vão de 0 a 9. Para esses números no sistema decimal existem símbolos: 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. Obviamente, muitos programadores iniciantes terão dúvidas sobre como escrever números maiores que 9 para o sistema de numeração hexadecimal. Neste ponto, os caracteres do alfabeto latino de A a F vêm em nosso auxílio. Portanto, para escrever valores no sistema de numeração hexadecimal, obtemos o seguinte conjunto de caracteres: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Agora podemos entenderfacilmente como será o byte onde está escrito o valor do canal por meio do sistema de numeração hexadecimal ou formato HEX. Em linguagens de programação semelhantes a C, HEX é escrito como 0x[valor]. Assim, o intervalo de um byte terá valores de 0x00 a 0xFF.


    Exemplos de criação de gráficos com CCanvas

    Vamos voltar ao tópico do formato de pixel e ver o exemplo mais simples de um aplicativo com uso de CCanvas. Também vamos experimentar diferentes valores para o formato de pixel, tentando definir os valores de cor e canal alfa da imagem. Vamos começar com o formato mais versátil, que é o COLOR_FORMAT_ARGB_NORMALIZE. Vamos usá-lo, para construir nosso primeiro aplicativo.

    Aplicativo simples usando o CCanvas

    Vamos criar um script e chamá-lo Erase.mq5.

    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(ColorToARGB(clrWhite, 255));
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Na primeira linha vemos a incorporação do módulo Canvas.mqh. Agora podemos criar um objeto, uma instância da classe CCanvas,para continuar a trabalhar, como feito no exemplo.

    CCanvas  canvas;

    Em seguida, criamos o próprio Canvas com o parâmetro clrfmt: COLOR_FORMAT_ARGB_NORMALIZE.

     canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);

    E o preenchemos com o método Erase.

     canvas.Erase(ColorToARGB(clrWhite, 255));

    Após a criação do Canvas, chamamos o método Update.

    canvas.Update(true);

    Usamos o parâmetro redraw: true. Ele pode ser omitido, pois já está definido por padrão, mas sugiro fazer isso para maior clareza. Depois, esperamos 6 segundos para ver como funciona o aplicativo no gráfico.

    Sleep(6000); 

    Em seguida, chamamos o método Destroy para liberar a memória ocupada pelo recurso gráfico e pelo objeto gráfico.

    canvas.Destroy();

    Assim, o script termina seu trabalho. O resultado pode ser visto na imagem abaixo.

    Erase

    Aqui vemos um retângulo preenchido que pode ser usado como base ou plano de fundo para desenhar construções gráficas mais complexas.

    Formato de pixel e métodos de sobreposição

    Vamos pegar nosso exemplo para Erase.mq5 e tentar definir uma cor diretamente, sem a função ColorToARGB. Vamos copiar todo o código do exemplo e criar um script chamado ARGB_NORMALIZE.mq5 baseado nele. Agora vamos definir a cor escolhendo uma das cores na paleta de cores que vimos. Por exemplo, clrPaleGreen. Vamos pegar seu valor no formato HEX e adicionar o valor do canal alfa 0xFF à esquerda.

    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(0xFF98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Na listagem, os componentes RGB são destacados em verde, o componente A é destacado em cinza. Vamos executar o script e ver o que acontece.

    Alteração da cor no formato COLOR_FORMAT_ARGB_NORMALIZE

    Vemos como a cor da imagem mudou. Agora vamos tentar mudar a transparência. Definimos o valor do canal alfa como 0xCC.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(0xCC98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    E vemos o resultado.

    Alterando a opacidade no formato COLOR_FORMAT_ARGB_NORMALIZE

    Na imagem, podemos ver que a área sombreada ficou translúcida.

    Vamos alterar o formato de pixel para COLOR_FORMAT_ARGB_RAW e dar a opacidade total à imagem. Para fazer isso, vamos criar um exemplo separado ARGB_RAW.mq5.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
        canvas.Erase(0xFF98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Vamos executar o exemplo e ver o resultado.

    Opacidade no formato COLOR_FORMAT_ARGB_RAW

    Vemos que o resultado não é diferente do exemplo COLOR_FORMAT_ARGB_NORMALIZE.

    Definimos o canal alfa como 0xFB e alteramos a cor de fundo do gráfico para branco.

    void OnStart()
     {
        ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
        canvas.Erase(0xFB98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Como resultado, vemos apenas uma pequena alteração e nenhuma diferença com o formato de pixel COLOR_FORMAT_ARGB_NORMALIZE.

    Alterando a opacidade no formato COLOR_FORMAT_ARGB_RAW

    Porém, vale a pena reduzir o valor do canal alfa em apenas um,

    canvas.Erase(0xFA98FB98);

    porque assim notamos imediatamente mudanças bastante significativas na imagem final.

    Pequena alteração na opacidade no formato COLOR_FORMAT_ARGB_RAW

    Agora vamos verificar como a imagem se comporta quando a transparência é completamente alterada. Para fazer isso, vamos criar um novo exemplo e chamá-lo de ARGB_RAW-2.mq5. Vamos copiar o código do exemplo anterior e fazer pequenas alterações nele para que o valor do canal alfa possa mudar de 255 para 0 em um determinado intervalo.

    void OnStart()
     {
      ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
      CCanvas canvas;
      canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(0xFF98FB98);
      for(int a = 255; a >= 0; a--)
       {
        canvas.TransparentLevelSet((uchar)a);
        canvas.Update(true);
        Sleep(100);
       }
      canvas.Destroy();
     }
    

    Como você pode ver na listagem, um loop for foi adicionado ao aplicativo, ele serve para alterar a transparência (a variável a do loop) chamando o método TrancparentLevelSet ealterando a transparência de toda a imagem.

    canvas.TransparentLevelSet((uchar)a);

    Em seguida, o método Update é chamado.

    canvas.Update(true);

    A seguir, a função Sleep faz com que o loop espere 100 milissegundos (para que o usuário tenha tempo de ver a mudança na transparência).

    Sleep(100);

    Depois, o aplicativo remove o Canvas

    canvas.Destroy();

    e conclui seu trabalho. O resultado pode ser visto na animação GIF.

    Alteração de opacidade total no formato COLOR_FORMAT_ARGB_RAW

    A partir do exemplo acima, assim como de todos os exemplos usando o formato COLOR_FORMAT_ARGB_RAW, vimos que uma imagem com valor alfa de 255 é exibida corretamente. Mas vale a pena reduzir esse valor, pois a imagem começa a ser exibida com distorções, pois nesse formato não há normalização dos valores dos canais RGB que podem transbordar e, assim, criar artefatos e distorções, pois os valores transbordados acima de 255 são simplesmente cortados. Mas a imagem que usa este formato é renderizada mais rápido do que usando o formato COLOR_FORMAT_ARGB_NORMALIZE.

    Vamos voltar ao nosso exemplo que usa o formato de pixel COLOR_FORMAT_ARGB_RAWARGB_RAW.mq5. Vamos criar um script chamado XRGB_NOALPHA.mq5 e copiar o código de ARGB_RAW.mq5 para ele. Para fazer isso, definimos o formato de pixel como COLOR_FORMAT_XRGB_NOALPHA e também definimos o valor do canal alfa como zero no método Erase. 

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_XRGB_NOALPHA);
        canvas.Erase(0x0098FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Vamos executar o script e ver o resultado.

    Sobreposição de cores no formato COLOR_FORMAT_XRGB_NOALPHA

    Vemos que o resultado não é diferente dos exemplos com os formatos COLOR_FORMAT_ARGB_NORMALIZE e COLOR_FORMAT_ARGB_RAW, com o valor máximo do canal alfa (0xFF) no método Erase. Assim, vemos que neste formato, o valor do canal alfa é completamente ignorado e a imagem é simplesmente sobreposta à imagem do gráfico. 

    Exibição de texto

    Sabemos que o CCanvas possui um método TextOut para exibir texto. Vamos tentar exibir o texto. Vamos criar um script e nomeá-lo TextOut.mq5. Vamos tomar como base o exemplo Erase.mq5, analisado anteriormente, e copiamos seu código. Vamos adicionar um método para definir os parâmetros da fonte FontSet e definir os valores desejados, onome da fonte é "Calibri" e o tamanho da fonte é -210.

    Para definir o tamanho da fonte em pixels, o tamanho nos métodos FontSet eFontSizeSet deve ser multiplicado por -10. Portanto, para um tamanho de fonte de 21 pixels, o tamanho seria -210.

    Os demais parâmetros serão deixados como padrão. Em seguida, vamos adicionar o método TextOut com o parâmetro text: "Texto". Todas as outras linhas necessárias para que este exemplo funcione são deixadas no código copiado.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(ColorToARGB(clrWhite, 255));
        canvas.FontSet("Calibri", -210);
        canvas.TextOut(15, 15, "Text", ColorToARGB(clrLightSlateGray, 255));
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Vamos ver o resultado do script.

    Exibição de texto com o método TextOut

    Podemos ver que o texto apareceu na imagem. 

    Vamos alterar nosso exemplo para que a imagem apareça exatamente no centro do gráfico e se assemelhe a um objeto Label (OBJ_LABEL). Vamos criar um novo exemplo baseado neste script e chamá-lo de TextOut-2.mq5.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_NORMALIZE);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    A listagem mostra que o texto exibido é gravado na variável text (porque é acessado duas vezes no código do script).

    string text = "Text";

    As variáveis width e height são declaradas para armazenar o tamanho do texto exibido.

    int width, height;

    São declaradas as constantes textXDist e textYDist, que armazenam os recuos do texto exibido em relação às bordas direita e superior da imagem, respectivamente.

    const int textXDist = 10, textYDist = 5;

    Em seguida, as variáveis canvasX e canvasY são declaradas para conter os resultados dos cálculos das coordenadas da imagem.

    int canvasX, canvasY;

    Em seguida, o método FontSet é chamado, para determinar as configurações da fonte (para predefini-las).

    canvas.FontSet("Calibri", -210);

    Em seguida, com o método TextSize, são determinadas as dimensões do texto exibido na tela (para entender quais tamanhos a imagem deve ter), que são armazenadas nas variáveis de width e height.

    canvas.TextSize(text, width, height);

    Em seguida, as coordenadas da imagem são calculadas para a imagem ser exibida no centro do gráfico.

    canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
    canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
    

    Em seguida, uma imagem é criada por meio do método CreateBitmapLabel, onde são definidas as coordenadas calculadas anteriormente.

    canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_NORMALIZE);
    

    A seguir, imagem é preenchida com cor usando o método Erase.

    canvas.Erase(ColorToARGB(clrWhite, 255));

    O texto (text) é então gerado usando o método TextOut, especificando as coordenadas textXDist definidas anteriormente e textYDist.

    canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));

    Depois, há linhas já familiares para nós, que não precisam de esclarecimento. Vamos executar o script e ver o resultado.

    Exemplo de rótulo

    Na imagem, vemos um objeto parecido com um rótulo. Mas lhe falta um fundo transparente. Ele também pode ser definido em nosso formato de pixel atual COLOR_FORMAT_ARGB_NORMALIZE. Mas faremos isso de modo diferente. Vamos configurar nosso Canvas para um formato de pixel rápido COLOR_FORMAT_ARGB_RAW, pois neste caso não precisamos de mistura de cores, e cores com valor de canal alfa de 0 e 255 neste formato serão exibidas sem distorção. Alem disso, uma cor com um valor alfa de 0 não afetará a imagem final. Vamos copiar o script LabelExample.mq5 com base no exemplo anterior.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.FontSet("Calibri", -210);
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Não alteramos o valor do canal alfa para garantir o acima (que o valor do canal alfa de 255 recolorirá completamente os pixels do gráfico).

    Verificação de formato COLOR_FORMAT_ARGB_RAW

    Agora vamos definir o valor do canal alfa como 0.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(ColorToARGB(clrWhite, 0));
      canvas.FontSet("Calibri", -210);
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Vamos executar o script e ver o que obtemos.

    Rótulo

    Vemos um análogo de um rótulo de texto (OBJ_LABEL). Assim, podemos exibir texto em cima da imagem sem repintá-la completamente, o que é amplamente utilizado em todos os tipos de interface do usuário para designar controles e simplesmente exibir informações de texto.

    Desenhando primitivas simples sem antisserrilhamento

    Para entender como desenhar primitivas no CCanvas, consideremos o exemplo DrawPrimitives.mq5.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.LineHorizontal(point, w - point, h - point, color_);
      canvas.LineVertical(point, point, h - point, color_);
      canvas.Line(point * 2, point * 13, point * 8, point * 9, color_);
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.Polyline(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.Polygon(pgX, pgY, color_);
      canvas.Rectangle(point * 2, point * 5, point * 7, point, color_);
      canvas.Triangle(point * 2, point * 11, point * 2, point * 6, point * 7, point * 6, color_);
      canvas.Circle(point * 10, point * 3, point * 2, color_);
      canvas.Ellipse(point * 8, point * 9, point * 12, point * 6, color_);
      canvas.Arc(point * 15, point * 2, point * 2, point, 45.0 * M_PI / 180, 180.0 * M_PI / 180, color_);
      canvas.Pie(point * 16, point * 3, point * 2, point, 180.0 * M_PI / 180, 402.5 * M_PI / 180, color_, fillColor);
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    É conveniente desenhar todas as primitivas por células, da mesma forma que é conveniente definir suas coordenadas e outros parâmetros. Por isso, decidi simplificar a tarefa dividindo a imagem do CCanvas em partes iguais em largura e altura para poder definir as coordenadas nas células e depois convertê-las em pixels multiplicando as coordenadas nas células pelo tamanho de uma célula. Para fazer isso, peguei as dimensões do gráfico como as dimensões da imagem do CCanvase dividi o menor tamanho por 15.

      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    

    Na listagem, este código está destacado em amarelo. Agora, sabendo o tamanho de uma célula (a variável point), podemos converter as coordenadas especificadas nas células em seus valores naturais em pixels. Antes de criar este exemplo, desenhei a imagem do gráfico em células e escrevi as coordenadas de todas as primitivas no script usando-as.

    int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
    int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
    

    Aqui eu defino as coordenadas para a polilinha (Polyline) e para o polígono (Polygon) em matriz. Em seguida, eu os converti em pixels, fazendo um loop pelas matrizes, e desenhei uma polilinha e um polígono.

      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.Polyline(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.Polygon(pgX, pgY, color_);
    

    O código para chamar métodos para desenhar primitivas é destacado em amarelo. O restante das primitivas é gerado da seguinte forma.

      canvas.Polygon(pgX, pgY, color_);
      canvas.Rectangle(point * 2, point * 5, point * 7, point, color_);
      canvas.Triangle(point * 2, point * 11, point * 2, point * 6, point * 7, point * 6, color_);
      canvas.Circle(point * 10, point * 3, point * 2, color_);
      canvas.Ellipse(point * 8, point * 9, point * 12, point * 6, color_);
      canvas.Arc(point * 15, point * 2, point * 2, point, 45.0 * M_PI / 180, 180.0 * M_PI / 180, color_);
      canvas.Pie(point * 16, point * 3, point * 2, point, 180.0 * M_PI / 180, 402.5 * M_PI / 180, color_, fillColor);
    

    Como você pode ver na listagem, para converter em pixels, todas as coordenadas são multiplicadas por point. Vejamos o resultado do script.

    Primitivas simples

    Vemos primitivas com um estilo de desenho de linha simples (STYLE_SOLID) que não pode ser alterado.

    Desenhando primitivas com antisserrilhamento e estilo de linha variável

    Para alterar o estilo da linha, usaremos métodos com o algoritmo de filtragem por meio do antisserrilhamento (AA). Vamos criar um script chamado DrawPrimitivesAA.mq5 e copiar o código do exemplo anterior para ele. Todos os métodos que existem na classe CCanvas com o prefixo AA (LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA e EllipseAA) serão deixados em total conformidade. O resto dos métodos serão removidos. Como há menos primitivas em comparação com o exemplo anterior, vamos alterar sua localização.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.LineStyleSet(lineStyle);
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.PolylineAA(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.PolygonAA(pgX, pgY, color_);
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_);
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    As linhas que foram alteradas são destacadas em amarelo. Agora vamos executar o script e ver o resultado.

    Primitivas com antisserrilhamento

    Na imagem vemos o resultado dos métodos com antisserrilhamento. Se observarmos atentamente as linhas, elas parecem mais suaves em comparação com o resultado do exemplo anterior, com primitivas simples.

    Vimos o resultado de métodos primitivas de desenho usando antisserrilhamento, mas o estilo de linha ainda é STYLE_SOLID . Vamos corrigir isso criando um script chamado DrawPrimitivesAA-2.mq5, colando o código do exemplo anterior lá. Vamos escrever o parâmetro sleep como um parâmetro de entrada, que definirá o atraso depois de alterar o estilo da linha e a exibição de primitivas.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    

    Vamos colocar todos os métodos de desenho em uma macro.

    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineAA(plX, plY, color_);                                                         \
      canvas.PolygonAA(pgX, pgY, color_);                                                          \
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    

    Em seguida, alternadamente vamos mudar o estilo de desenho de linha usando o método LineStyleSet e exibir primitivas. Vejamos o exemplo resultante.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineAA(plX, plY, color_);                                                         \
      canvas.PolygonAA(pgX, pgY, color_);                                                          \
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      //ENUM_LINE_STYLE lineStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DOT);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DASH);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DASHDOTDOT);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    No trecho de código destacado, podemos ver como o estilo muda e como os métodos de desenho são chamados. Vamos ver o resultado do script.

    Mudança de estilo de desenho de linha (AA)

    Nesta animação GIF, vemos como o estilo de linha das primitivas muda com um determinado intervalo sleep.

    Agora proponho criar um exemplo de primitivas de desenho usando o algoritmo Wu. Vamos criar o script DrawPrimitivesWu.mq5 com base no exemplo anterior. Substituímos a combinação de caracteres AA por Wu e comentamos as linhas, conforme mostrado na listagem.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineWu(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineWu(plX, plY, color_);                                                         \
      canvas.PolygonWu(pgX, pgY, color_);                                                          \
      canvas.TriangleWu(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleWu(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseWu(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      //ENUM_LINE_STYLE lineStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      //canvas.LineStyleSet(STYLE_DOT);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASH);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASHDOTDOT);
      //drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Todos os métodos adicionados são destacados em amarelo. Vamos executar o exemplo e ver o resultado.

    Primitivas com antisserrilhamento Wu

    Podemos ver que a qualidade do desenho ficou ainda melhor, graças ao antisserrilhamento usando o algoritmo Wu. Agora vamos descomentar as linhas em nosso script e executá-lo novamente.

    Alterando o estilo de desenho de linha (Wu)

    Vemos que os estilos de linha mudam da mesma forma que no exemplo anterior, com a única diferença de que a qualidade de suavização da imagem das primitivas é maior.

    Desenho de primitivas com espessura de linha variável

    Vamos criar um script baseado no exemplo anterior e chamá-lo de DrawPrimitivesThick.mq5. Vamos substituir os métodos existentes para desenhar primitivas por métodos com o prefixo "Thick" e remover aqueles métodos que não possuem análogos, conforme mostrado na listagem. E como no exemplo anterior, comentamos as linhas desnecessárias.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                                      \
      canvas.LineThickVertical(point, point, h - point, color_, size, lineStyle, endStyle);               \
      canvas.LineThickHorizontal(point, w - point, h - point, color_, size, lineStyle, endStyle);         \
      canvas.LineThick(point * 2, point * 12, point * 8, point * 8, color_, size, lineStyle, endStyle);   \
      canvas.PolylineThick(plX, plY, color_, size, lineStyle, endStyle);                                  \
      canvas.PolygonThick(pgX, pgY, color_, size, lineStyle, endStyle);                                   \
      canvas.Update(true);                                                                                \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {17, 18, 17, 15, 14, 15, 17}, pgY[] = {7, 5, 3, 3, 5, 7, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_ROUND;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      //canvas.LineStyleSet(STYLE_DOT);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASH);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASHDOTDOT);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Na listagem, os métodos adicionados são destacados em amarelo. Como os leitores atentos já notaram, os métodos em consideração têm dois parâmetros adicionais: size, que é a espessura da linha e end_style, que é o estilo do final da linha. Vamos executar o script e ver o resultado.

    Exibição de primitiva (Thick)

    Na imagem, vemos primitivas com linhas grossas, cuja espessura pode ser alterada, conforme mencionado acima. Vamos criar um novo script baseado no anterior e chamá-lo de DrawPrimitivesThick-2.mq5. Com ele, veremos todas as combinações possíveis de espessura de linha, estilos de linha e estilos de conclusão de linha. Para fazer isso, descomentamos as linhas que foram comentadas e as adicionamos à macro method1, onde mudaremos o estilo do final das linhas e chamaremos a macro method0 após cada alteração. Na macro method0, chamaremos os métodos de desenho das primitivas. Na macro drawPrimitives, vamos mudar o estilo das linhas e chamar method1 após cada mudança no estilo das linhas. A macrodrawPrimitivas será chamada em um loop onde mudaremos a espessura das linhas em um determinado intervalo.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    input int beginSize = 1;
    input int endSize = 4;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives lineStyle = STYLE_SOLID; \
      method1                                       \
      lineStyle = STYLE_DOT;                        \
      method1                                       \
      lineStyle = STYLE_DASH;                       \
      method1                                       \
      lineStyle = STYLE_DASHDOTDOT;                 \
      method1 
      
    #define method1 endStyle = LINE_END_ROUND; \
      method0                                  \
      endStyle = LINE_END_BUTT;                \
      method0                                  \
      endStyle = LINE_END_SQUARE;              \
      method0
    
    #define method0 canvas.Erase(ColorToARGB(clrWhite, 255));                                                                                                      \
      canvas.LineThickVertical(point, point, h - point, color_, size, lineStyle, endStyle);                                                                        \
      canvas.LineThickHorizontal(point, w - point, h - point, color_, size, lineStyle, endStyle);                                                                  \
      canvas.LineThick(point * 2, point * 12, point * 8, point * 8, color_, size, lineStyle, endStyle);                                                            \
      canvas.PolylineThick(plX, plY, color_, size, lineStyle, endStyle);                                                                                           \
      canvas.PolygonThick(pgX, pgY, color_, size, lineStyle, endStyle);                                                                                            \
      canvas.TextOut(point * 2, point, "Size: " + (string)size + "; Style: " + EnumToString(lineStyle) + "; End Style: " + EnumToString(endStyle) + ";", color_);  \
      canvas.Update(true);                                                                                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {17, 18, 17, 15, 14, 15, 17}, pgY[] = {7, 5, 3, 3, 5, 7, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_ROUND;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      for (int i = beginSize; i <= endSize; i++) {
        size = i;
        drawPrimitives
      }
      
      Sleep(6000);
      canvas.Destroy();
     }
    //+------------------------------------------------------------------+
    

    Todas as alterações no código em comparação com o exemplo anterior são destacadas em amarelo. O script resultante usa um intervalo de larguras de linha a partir dos parâmetros de entrada beginSize e endSize. E percorre, com a ajuda de macros, todas as combinações possíveis de estilo da linha e de estilo de conclusão de linha. O resultado é uma enumeração de todos os parâmetros possíveis de linhas primitivas, cujas combinações podem ser vistas executando o script. O resultado do script é mostrado na animação GIF a seguir.

    Primitivas com parâmetros diferentes

    Na animação, vemos como a espessura da linha, o estilo da linha e o estilo de conclusão da linha mudam. Todos esses parâmetros são exibidos no Canvas, e também podemos vê-los.

    Desenhando primitivas suavizadas usando o algoritmo Bezier com espessura de linha variável

    Vamos pegar um de nossos exemplos anteriores DrawPrimitivesThick.mq5 e criar um script baseado nele. Vamos chamá-lo de DrawPrimitivesSmooth.mq5. No script, substituímos os métodos PolylineThick e PolygonThick por PolylineSmooth ePolygonSmooth. Vamos deslocar as coordenadas da polilinha e do polígono em 2 células para a esquerda para que as primitivas sejam exibidas aproximadamente no centro.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
    

    A listagem destaca os valores que foram alterados. Como resultado, devemos obter um script com o seguinte conteúdo.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                                      \
      canvas.PolylineSmooth(plX, plY, color_, size, lineStyle, endStyle);                                 \
      canvas.PolygonSmooth(pgX, pgY, color_, size, lineStyle, endStyle);                                  \
      canvas.Update(true);                                                                                \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_BUTT;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Vamos executar o exemplo resultante e ver o resultado.

    Primitivas de Bezier (suave)

    Na imagem, vemos uma polilinha e um polígono, que são construídos usando curvas de Bezier. Os métodos PolylineSmooth ePolygonSmooth possuem os parâmetros tension (suavização) e step (passo de aproximação), segundo os quais as curvas de Bezier são construídas. Vamos criar um exemplo onde esses parâmetros serão alterados em intervalos especificados e o resultado dessas alterações será exibido na tela. Vamos pegar o exemplo DrawPrimitivesThick-2.mq5 e criar o script DrawPrimitivesSmooth-2.mq5 baseado nele. Para não tardar com o processo de alteração do código, vamos ver imediatamente o resultado.

    #property script_show_inputs
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //--- input parameters
    input int sleep = 1000;
    input int lineSize = 3;
    input ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
    input ENUM_LINE_END lineEndStyle = LINE_END_BUTT;
    input double minTension = 0.0;
    input double stepTension = 0.1;
    input double maxTension = 1.0;
    input double minStep = 1.0;
    input double stepStep = 5.0;
    input double maxStep = 21.0;
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE style = lineStyle;
      int size = lineSize;
      ENUM_LINE_END endStyle = lineEndStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      for (double tension = minTension; tension <= maxTension; tension += stepTension)
        for (double step = minStep; step <= maxStep; step += stepStep)
         {
           canvas.Erase(ColorToARGB(clrWhite, 255));
           canvas.PolylineSmooth(plX, plY, color_, size, style, endStyle, tension, step);
           canvas.PolygonSmooth(pgX, pgY, color_, size, style, endStyle, tension, step);
           canvas.TextOut(point * 2, point, "Size: " + (string)size + "; Style: " + EnumToString(style) + "; End Style: " + EnumToString(endStyle) + ";", color_);
           canvas.TextOut(point * 2, point * 2, "Tension: " + DoubleToString(tension, 2) + ";" + " Step: " + DoubleToString(step, 2) + ";", color_);
           canvas.Update(true);
           Sleep(sleep);
         }
      
      canvas.Destroy();
     }
    

    Podemos ver que o código foi quase completamente alterado. As seguintes configurações apareceram entre os parâmetros de entrada: lineSize, que é o tamanho da linha, lineStyle, que é o estilo da linha, e lineEndStyle, que é o estilo do final da linha (agora eles não participam do loop). Também são adicionados parâmetros de entrada para definir os parâmetros de desenho da curva Bezier: minTension, maxTension e stepTension (intervalo e passo de valores do parâmetro antisserrilhamento tension), bem como minStep, maxStep e stepStep (intervalo e passo de valores do passo de aproximação step). Além disso, esses parâmetros funcionam em loops, definindo todas as combinações possíveis de parâmetros tension e step de acordo com o stepTension fornecido e stepStep, respectivamente. Vamos executar o exemplo e ver o que obtemos como resultado.

    Primitivas com parâmetros diferentes

    Nesta animação GIF, vemos primitivas antisserrilhamento com vários parâmetros antisserrilhamento, cujos valores são exibidos na imagem, na segunda linha.

    Desenho de primitivas sombreadas

    Ao desenvolver aplicativos que exibem gráficos, há momentos em que precisamos pintar sobre uma ou outra primitiva. Na classe CCanvac, para esses casos, existem métodos com o prefixo Fill, que pintam as primitivas correspondentes em uma cor uniforme. Vamos pegar nosso exemplo anterior DrawPrimitivesWu.mq5 e criar o script DrawPrimitivesFill.mq5 baseado nele, onde todas as coordenadas para as primitivas que precisamos já estão escritas e resta apenas substituir os métodos correspondentes e definir a cor de preenchimento. Substituímos os métodos FillPolygon, FillTriangle, FillCircle e FillEllipse. O método Fill será discutido abaixo (quando vejamos o preenchimento). Vamos ver o código resultante.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint fillColor = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.FillPolygon(pgX, pgY, fillColor);
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, fillColor); 
      canvas.FillCircle(point * 13, point * 12, point * 2, fillColor);
      canvas.FillEllipse(point * 11, point * 3, point * 15, point * 6, fillColor);
      canvas.Update(true);                                                                         
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Todas as linhas alteradas e adicionadas são destacadas em amarelo. Vamos executar o script e ver o resultado.

    Primitivas preenchidas (Fill) 

    Na imagem, vemos primitivas preenchidas com uma cor uniforme onde podemos desenhar primitivas comuns como um traço ou primitivas com um estilo de linha variável, que discutimos acima.

    Preenchimento

    Todo mundo que já usou a ferramenta "Preencher" em editores gráficos provavelmente pode adivinhar sobre o que vamos falar agora. Bem, é hora de olhar para o método Fill, tratado acima, que apenas preenche uma área da imagem com uma determinada cor. Vamos criar um novo exemplo na forma de um indicador (porque ele processa eventos do gráfico (OnChartEvent) ao contrário de um script). Muitos perguntarão por que razão precisamos processar eventos de gráfico neste caso. Vamos criar um exemplo onde podemos selecionar uma cor de preenchimento e, clicando em qualquer lugar da imagem, preenchê-la com a cor selecionada. Vamos criar um novo indicador e chamá-lo de Filling.mq5. Vamos considerar o código do indicador.

    #property indicator_chart_window
    #property indicator_plots 0
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    #include <ChartObjects\ChartObjectsTxtControls.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    CCanvas canvas;
    CChartObjectButton colors[14];
    CChartObjectButton * oldSelected = NULL;
    uint fillColor = 0;
    
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
     {
      int w, h;
      int minSize, point;
      uint Color1 = ColorToARGB(clrDodgerBlue, 255);
      uint Color2 = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.Polygon(pgX, pgY, Color1);
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, Color2);
      canvas.FillCircle(point * 13, point * 12, point * 2, Color2);
      canvas.Ellipse(point * 11, point * 3, point * 15, point * 6, Color1);
      canvas.Update(true); 
      
      ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
      
      for (int i = 0; i < (int)colors.Size(); i++)
       {
        colors[i].Create(0, "color-"+(string)i, 0, (int)((i + 2.8) * point), point, 21, 21);
        colors[i].SetInteger(OBJPROP_BGCOLOR, ColorToARGB(CCanvas::GetDefaultColor(i), 0));
       }
      
      if ((oldSelected = GetPointer(colors[(int)colors.Size()-1])) == NULL)
        return(INIT_FAILED); 
      oldSelected.State(1);  
      fillColor = ColorToARGB((color)oldSelected.GetInteger(OBJPROP_BGCOLOR), 255);
      
      return(INIT_SUCCEEDED);
     }
     
    //+------------------------------------------------------------------+
    //| Custom indicator deinitialization function                       |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
      canvas.Destroy();
     }
     
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const int begin,
                    const double &price[])
     {
    //---
    //--- return value of prev_calculated for the next call
      return(rates_total);
     }
    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
     {
      uint mouseState = (uint)sparam;
      int x = (int)lparam, y = (int)dparam;
      CChartObjectButton * colorBtn;
      int left, right, bottom, top;
      
      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          for (int i = 0; i < (int)colors.Size(); i++) 
           {
            if ((colorBtn = GetPointer(colors[i])) == NULL)
              return;
            left = colorBtn.X_Distance();
            top = colorBtn.Y_Distance();
            right = left + colorBtn.X_Size() - 1;
            bottom = top + colorBtn.Y_Size() - 1;
            if (x >= left && x <= right && y >= top && y <= bottom) {
              fillColor = ColorToARGB((color)colorBtn.GetInteger(OBJPROP_BGCOLOR), 255);
              if (oldSelected == NULL)
                return;
              oldSelected.State(0);
              oldSelected = GetPointer(colorBtn);
              ChartRedraw();
              return;
            }
           }
          canvas.Fill(x, y, fillColor);
          canvas.Update(true);
         }
        
     }
    

    Como funciona o exemplo? Primeiro, a variável canvas foi escrita em nível global para acesso a ela durante toda a operação do indicador.

    CCanvas canvas;

    Para poder selecionar cores, foi criado uma matriz de 14 botões, e a cor de preenchimento será ao pressionar um desses botões.

    CChartObjectButton colors[14];

    Em seguida, o ponteiro oldSelected é declarado.

    CChartObjectButton * oldSelected = NULL;

    A seguinte variável existe para manter o botão pressionado, retorná-lo à sua posição original quando uma cor diferente for selecionada e também para salvar a cor de preenchimento.

    uint fillColor = 0;

    Além disso, no manipulador OnInit, vemos o código já familiar que cria um Canvas e desenha primitivas nele.

    int OnInit()
     {
      int w, h;
      int minSize, point;
      uint Color1 = ColorToARGB(clrDodgerBlue, 255);
      uint Color2 = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));                                                                                                                 
      canvas.Polygon(pgX, pgY, Color1);                                                          
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, Color2); 
      canvas.FillCircle(point * 13, point * 12, point * 2, Color2);                                  
      canvas.Ellipse(point * 11, point * 3, point * 15, point * 6, Color1);                     
      canvas.Update(true);
    ...
    

    Em seguida, a manipulação de eventos do mouse é habilitada.

    ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

    Depois, são criados botões de cores diferentes (para poder alterar a cor de preenchimento).

      for (int i = 0; i < (int)colors.Size(); i++)
       {
        colors[i].Create(0, "color-"+(string)i, 0, (int)((i + 2.8) * point), point, 21, 21);
        colors[i].SetInteger(OBJPROP_BGCOLOR, ColorToARGB(CCanvas::GetDefaultColor(i), 0));
       }
    

    Sugiro que você preste atenção de onde vêm as cores dos botões. No código, este fragmento é destacado em amarelo. Vemos o método estático CCanvas::GetDefaultColor. Ao definir o parâmetro i, a partir de 0, podemos obter uma paleta de cores, o que é feito neste exemplo.

    Em seguida, é inicializado um link para redefinir o botão oldSelected para seu estado original.

      if ((oldSelected = GetPointer(colors[(int)colors.Size()-1])) == NULL)
        return(INIT_FAILED); 
      oldSelected.State(1);
    

    E fillColor é inicializado.

    fillColor = ColorToARGB((color)oldSelected.GetInteger(OBJPROP_BGCOLOR), 255);

    Em seguida, os eventos do gráfico são processados em OnChartEvent. Ele declara variáveis para obter e armazenar parâmetros do mouse.

    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
     {
      uint mouseState = (uint)sparam;
      int x = (int)lparam, y = (int)dparam;
    ...
    

    A seguir, é declarada uma variável, o ponteiro para armazenar o botão pressionado,

    CChartObjectButton * colorBtn;

    que é determinado pela ocorrência do cursor nas coordenadas dos lados do botão ao clicar nele com o botão esquerdo do mouse. As coordenadas laterais são armazenadas nas seguintes variáveis.

    int left, right, bottom, top;

    Em seguida, o evento CHARTEVENT_MOUSE_MOVE e o pressionamento do botão esquerdo do mouse são monitorados.

      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          ...
         }
    

    Este é o código no qual é realizada a escolha da cor e do preenchimento da imagem. Aqui, o loop percorre todos os botões na matriz colors, determinando em qual botão o usuário clicou, armazenando a cor desse botão na variável fillColor e retornando o botão oldSelected pressionado anteriormente para sua posição original. Em seguida, o manipulador é encerrado (return), pois o clique foi no botão e não na imagem que precisa ser preenchida. Se o clique foi feito na imagem, e não em um dos botões decores, o controle é passado e é preenchido pelo método Fill com o fillColor selecionado seguido da atualização da imagem (método Update).

      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          for (int i = 0; i < (int)colors.Size(); i++) 
           {
            if ((colorBtn = GetPointer(colors[i])) == NULL)
              return;
            left = colorBtn.X_Distance();
            top = colorBtn.Y_Distance();
            right = left + colorBtn.X_Size() - 1;
            bottom = top + colorBtn.Y_Size() - 1;
            if (x >= left && x <= right && y >= top && y <= bottom) {
              fillColor = ColorToARGB((color)colorBtn.GetInteger(OBJPROP_BGCOLOR), 255);
              if (oldSelected == NULL)
                return;
              oldSelected.State(0);
              oldSelected = GetPointer(colorBtn);
              ChartRedraw();
              return;
            }
           }
          canvas.Fill(x, y, fillColor);
          canvas.Update(true);
         }
    

    Agora sugiro que você execute o exemplo resultante e tente preenchê-lo.

    Preenchimento em CCanvas

    Como você pode ver na animação GIF apresentada, criamos um preenchimento usando o CCanvas, que funciona de maneira semelhante à ferramenta "Preenchimento" em editores gráficos.


    Conclusão

    Neste artigo, revisamos a estrutura da classe CCanvas, estudamos seus métodos e os revisamos em uma ordem que dá ao usuário uma compreensão completa dos princípios básicos da classe. Os exemplos considerados explicam claramente os princípios de trabalho com a classe CCanvas, confirmando o material considerado e esclarecendo aspectos de difícil compreensão na leitura da parte teórica do artigo.


    Traduzido do russo pela MetaQuotes Ltd.
    Artigo original: https://www.mql5.com/ru/articles/10361

    Arquivos anexados |
    MQL5.zip (21.43 KB)
    Últimos Comentários | Ir para discussão (2)
    Francisco Gomes Da Silva
    Francisco Gomes Da Silva | 29 jun 2022 em 16:04
    Muito obrigado pelo artigo, eu quero chegar nesse nível de excelência 
    Mihail Matkovskij
    Mihail Matkovskij | 15 ago 2022 em 20:21
    Nada é impossível. Tudo vem com o tempo.
    Tutorial DirectX (Parte I): Desenhando o primeiro triângulo Tutorial DirectX (Parte I): Desenhando o primeiro triângulo
    Este é um artigo introdutório sobre o DirectX, que descreve as especificidades da operação com a API. Ele deve ajudar a entender a ordem em que seus componentes são inicializados. O artigo contém um exemplo de como escrever um script MQL5 que renderiza um triângulo usando o DirectX.
    Desenvolvendo um EA de negociação do zero (Parte 20): Um novo sistema de ordens (III) Desenvolvendo um EA de negociação do zero (Parte 20): Um novo sistema de ordens (III)
    Vamos continuar a implementação do novo sistema de ordens . A criação deste sistema é algo que demanda um bom domínio do MQL5, além de entender como de fato a plataforma MetaTrader 5 funciona e os recursos que ela nos fornece.
    Analisando as razões pelas quais alguns EAs fracassam Analisando as razões pelas quais alguns EAs fracassam
    Neste artigo, analisaremos dados de moedas e tentaremos entender com isso por que os Expert Advisors podem mostrar bons resultados em alguns intervalos e, ao mesmo tempo, ter um desempenho ruim em outros.
    O padrão de design MVC e suas possibilidades de uso (Parte 2): Esquema de interação entre três componentes O padrão de design MVC e suas possibilidades de uso (Parte 2): Esquema de interação entre três componentes
    Este artigo dá continuação e complemento ao tópico que vimos no artigo anterior, isto é, ao padrão MVC em programas escritos em MQL. Neste artigo, estudaremos um possível esquema de interação entre esses três componentes.