Desenhando Medidores com Mostrador usando a classe CCanvas
Índice
- Introdução
- 1. Coordenadas e Âncora
- 2. Elementos do Medidor
- 3. Funções
- 3.1. GaugeCreate
- 3.2. GaugeSetCaseParameters
- 3.3. GaugeSetScaleParameters
- 3.4. GaugeSetMarkParameters
- 3.5. GaugeSetMarkLabelFont
- 3.6. GaugeSetLegendParameters
- 3.7. GaugeSetRangeParameters
- 3.8. GaugeSetNeedleParameters
- 3.9. GaugeRedraw
- 3.10. GaugeNewValue
- 3.11. GaugeDelete
- 3.12. GaugeCalcLocation
- 3.13. GaugeRelocation
- 4. Enumerações
- 4.1. ENUM_CASE_BORDER_STYLE
- 4.2. ENUM_CASE_STYLE
- 4.3. ENUM_GAUGE_LEGEND
- 4.4. ENUM_MARK_STYLE
- 4.5. ENUM_MUL_SCALE
- 4.6. ENUM_NCENTER_STYLE
- 4.7. ENUM_NEEDLE_FILL
- 4.8. ENUM_REL_MODE
- 4.9. ENUM_SCALE_STYLE
- 4.10. ENUM_SIZE
- 5. Substituição de Macro
- 6. Modificando a Classe CCanvas
- 6.1. Desenhando um Segmento Usando o Algoritmo de Anti-Aliasing
- 6.2. Preenchendo uma Área com Arestas Anti-Aliasing
- 7. Exemplos de Aplicação
- 8. Avaliação da Intensidade dos Recursos
- Conclusão
Introdução
Tudo começou quando eu comecei a me familiarizar com a classe CCanvas. No que diz a prática, eu me deparei com uma ideia em desenhar um indicador medidor com mostrador. Meus primeiros medidores foram bem grosseiros, mas, eventualmente, eles forma complementados por novos elementos e tornaram-se visualmente agradáveis. E, como resultado, eu tenho uma pequena biblioteca que agora permite adicionar um medidor a um indicador ou um EA de uma maneira simples e fácil. Neste artigo, vamos considerar a estrutura dos medidores, se familiarizar com as funções necessárias para desenhar e definir o aspecto visual, e avaliar a intensidade dos recursos.
Fig.1. Medidores com mostrador
1. Coordenadas e Âncora
Existem dois tipos de posicionar um medidor em um gráfico: absoluta e relativa.
No caso de posicionamento absoluto, as coordenadas representam as distâncias em pixeis a partir de uma âncora do canto ao longo dos eixos X e Y.
No caso de posicionamento relativo , a origem local de coordenadas é criada de acordo com o tipo específico do posicionamento relativo. Quando o tipo vertical é selecionado, a origem está localizada abaixo ou acima de um objeto de referência (se for selecionado um canto de ancoragem superior ou inferior, respectivamente). Quando o tipo vertical é selecionado, ela situa-se à esquerda ou à direita na direção do canto da âncora. Neste caso, as coordenadas especificadas representam um deslocamento a partir da sua origem local. Deslocamentos positivos levam ao movimento de um objeto para longe do objeto de referência. Em caso de desvios negativos, o objecto irá interferir no de referência.
O objeto de referência pode ser representado apenas por um objeto de outro medidor. É essencial que ambos os objetos tenham a mesmo âncora.
Fig. 2 representa um exemplo de posicionamento relativo.
Fig.2. O posicionamento relativo dos medidores
Vamos rever as configurações de cada medidor:
- O indicador "gg01": o posicionamento relativo está desativado. Deslocamento horizontal - 40, deslocamento vertical - 40.
- O indicador "gg02": posicionamento relativo - horizontal, objeto de referência - "gg01". Deslocamento horizontal das coordenadas do local de origem (ponto A) - 15, deslocamento vertical - 0.
- O indicador "gg03": posicionamento relativo - vertical, objeto de referência - "gg01". Deslocamento horizontal das coordenadas do local de origem (ponto B) - 0, deslocamento vertical - 15.
- O indicador "gg04": posicionamento relativo - vertical, objeto de referência - "gg02". Deslocamento horizontal das coordenadas do local de origem (ponto C) - 50, deslocamento vertical - 15.
O posicionamento relativo facilita a configuração de entrada se o gráfico tem vários indicadores que contenham medidores. Se você decidir alterar o tamanho de um medidor, as coordenadas de outros medidores serão recalculadas automaticamente.
A função GaugeCreate() define o tipo de posicionamento e as coordenadas.
2. Elementos do Medidor
O Medidor com mostrador é composto por dois objetos gráficos. Um deles é chamado de camada de escala, o outro é chamado de camada de agulha. Ambos os objetos gráficos têm as mesmas coordenadas. A camada de agulha é colocada sobre a camada de escala. O nome do medidor definido nos parâmetros de entrada serve como um prefixo para os nomes de ambos os objetos. Por exemplo, se o nome do medidor é o "Gauge01", a camada de escala será chamado "Gauge01_s", e a camada de agulha terá o nome "Gauge01_n".
Fig.3 mostra a estrutura do indicador.
Fig.3. Estrutura do medidor
A camada de escala contém:
- limite (1)
- marcas de graduação de escala (5, 6, 7)
- etiquetas de graduação de escala (4)
- intervalos destacados (2, 12)
- legendas (3, 10, 11)
As legendas são distinguidas por finalidades:
- Descrição do medidor (3)
- unidades de medida (11)
- valor atual (10)
- multiplicador de marcas de escala (omitido)
A graduação da escala é dividida em:
- principal (7)
- médio (5)
- menor (6)
Somente os pontos da graduação principal possuem etiquetas. O passo da graduação é definido como um valor numérico. O passo da graduação média é calculada de acordo com um determinado número de marcas médias entre os as principais. O passo da graduação menor é calculado de acordo com um determinado número de marcas menores entre as do meio. A graduação menor e do meio pode ser omitido.
A camada de agulha contém:
- agulha (8)
- centro da agulha (9)
Fig.3 mostra os tamanhos de alguns elementos do medidor:
- d - tamanho do medidor que corresponde ao diâmetro da linha de contorno externa do medidor
- b - tamanho da borda
- g - tamanho do espaço entre uma borda e os elementos de escala
- c - o tamanho do centro da agulha.
NB. O diâmetro do medidor é o único tamanho definido em pixels ("d" na fig.3). Todos os outros elementos e fontes são definidas em unidades condicionais e os seus tamanhos são calculados como a percentagem do diâmetro. Ela é feita para facilitar a escala. Altere o diâmetro, e todos os outros tamanhos serão proporcionalmente recalculados. Os coeficientes de cálculo estão listados na seção Substituição de Macro e pode ser modificado pelo utilizador.
Existem dois tipos de formas para o corpo do medidor: circular e de setor. A forma de setor é mais conveniente se o ângulo da faixa de escala é inferior a 180 graus.
Fig.4. Formato do medidor
Fig.4 descreve um medidor circular (a) e dois medidores na forma de setor (b, c). A função GaugeSetCaseParameters () é utilizada para definir a forma do corpo desejado.
Este é o elemento mais importante do indicador. A legibilidade dos dados depende da sua aparência. A escala não deve ser complicada, mas, ao mesmo tempo ela deve ser suficientemente informativa. A seleção de valores extremos da escala, bem como a etapa marcações principais, requerem uma atenção especial. A função GaugeSetScaleParameters() permite definir o intervalo de escala, a sua rotação e os valores extremos (mínimo e máximo). O valor mínimo pode ser à esquerda (ordem direta) ou à direita (ordem inversa).
A faixa de escala é um ângulo contido por dois vetores de raios de valores extremos da escala. Ela é demonstrada na Fig.5.
Fig.5. Faixa de escala
A escala de rotação é um ângulo de desvio da bissetriz angular da faixa de escala da linha que vem de cima e na vertical a partir do centro do medidor. Ela é demonstrada na fig.6.
Fig.6. Ângulo da escada de rotação
A combinação do ângulo da faixa de escala e o ângulo de rotação podem ajudá-lo a definir a aparência do medidor de uma forma bastante flexível. Fig.4(c) demonstra um medidor com um intervalo de 90 graus e 45 graus de rotação.
Os valores máximos e mínimos da escala são parâmetros importantes, que devem ser selecionados dependendo do intervalo dos valores permitidos da variável exibida. A marca zero pode ser omitida por uma questão de conveniência. Não há nenhuma razão de elaborar a escala do zero se as suas variáveis se alteram na faixa de 400-600. Fig.7 mostra alguns exemplos de valores máximos e mínimos da escala.
Fig.7. Os valores máximos e mínimos da escala
- a) valores de 0 a 500, ordem direta
- b) valores de -200 a 400, ordem direta
- c) valores de -400 a 0, ordem direta
- d) Valores de 500-0, ordem inversa
- e) valores de 200-800, ordem direta
- f) valores de 0 a -800, ordem inversa
A definição da marcação de graduação consiste em selecionar o tamanho das marcas e o método de alinhamento.
O alinhamento pode ser como se segue:
- borda interna da escala
- borda externa da escala
- centro
Fig.8 mostra exemplos da marcações de graduação da escala de alinhamento:
- a - centro
- b - borda interna
- c - borda externa
A função GaugeSetMarkParameters() é utilizada para definí-la.
A posição dos rótulos das marcações é referido as configurações de escala e ajustado utilizando a função GaugeSetScaleParameters().
Fig.8 (a) representa um exemplo de marcadores de posicionamento dentro da escala, Fig.8(b) e 8(c) - fora da escala.
Recomenda-se a utilização de um multiplicador, um coeficiente de todos os valores exibidos será dividido por ele, para que os rótulos não ocupem muito espaço na escala. O multiplicador pode ter valores de 0.0001 até 10000. A Fig.4(c) ilustra um exemplo da aplicação de um multiplicador igual a 100, o que permitiu a utilização dos números de um dígito, em vez dos números de três dígitos na etiquetas. Fig.1 descreve uma situação em que nós usamos um multiplicador igual a 0.0001 para o ATR, o que permitiu não utilizar o ponto decimal e zeros nas etiquetas. A função GaugeSetScaleParameters() define o multiplicador.
Fig.8. Marcações de posicionamento e etiquetas
As legendas são destinadas para a exibição de informações suplementares, podendo ser de quatro tipos:
- Descrição do medidor
- unidades de medida
- valor atual
- multiplicador
Qualquer legenda pode ser escondida. Apenas a descrição do medidor que é exibido por padrão.
O posicionamento da legenda é definido pelo ângulo e o raio. O ângulo é ajustado em graus e o seu valor é igual ao ângulo entre a linha que vem de cima e na vertical a partir do centro do medidor, e um segmento imaginário, que liga o centro do medidor e o centro da legenda. O raio é definido em unidades condicionais. Ele pode ter valores de 0 a 10, em que 0 corresponde ao raio do centro da agulha e 10 corresponde ao raio fora da escala.
Fig.9 representa um exemplo de posicionamento da legenda.
- A legenda "Profit" (descrição do medidor) tem as seguintes coordenadas: ângulo - 0, raio - 3.
- A legenda "0.00" (valor atual) tem as seguintes coordenadas: ângulo - 225, raio - 4.
- A legenda "USD" (unidades de medida) tem as seguintes coordenadas: ângulo - 215, raio - 8.
A função GaugeSetLegendParameters() é utilizada para definir os parâmetros da legenda.
Fig.9. Coordenadas da Legenda
NB. As legendas não são fixas na escala e seus ângulos não está conectados com o ângulo da escala de rotação.
As faixas dos dados destacadas representam um elemento inerente de qualquer medidor. Eles ajudam a ver que a variável tem assumido um valor de emergência ou entrou em alguma faixa especial. A função GaugeSetRangeParameters() pode definir até quatro faixas de destaque. Para fazer isso, você precisa definir os valores extremos e a cor para o destaque. Fig.1 mostra o indicador de lucro com duas faixas destacadas: de 200 para 400 é a faixa verde, que indica a hora de obter o lucro, e de -200 a -400 é a faixa laranja, advertindo sobre um grande rebaixamento.
A função GaugeSetNeedleParameters() define o tamanho do centro da agulha e do tipo de área de preenchimento. O tipo de preenchimento de área influência na intensidade de recursos do indicador, já que a camada da agulha está totalmente redesenhada toda vez após a atualização dos dados. Fig.10 ilustra um exemplo de preenchimento da área.
- agulha preenchida com a utilização do algoritmo de suavização (a)
- agulha preenchida sem a utilização do algoritmo de suavização (b)
- agulha não preenchida com a linha de contorno anti-aliasing (c)
Fig.10. Métodos de preenchimento da área da agulha
Os prós e contras de cada método são descritos nas seções dedicadas a modificação da classe CCanvas e na avaliação da intensidade de recursos.
3. Funções
A tabela 1 lista as funções de elaboração dos medidores e definem sua aparência.
Função | Comportamento |
---|---|
GaugeCalcLocation | Calcula as coordenadas do centro do medidor |
GaugeCreate | Cria o medidor |
GaugeDelete | Exclui o medidor |
GaugeNewValue | Atualiza a posição da agulha e o valor apresentado |
GaugeRedraw | Redesenha o medidor |
GaugeRelocation | Muda a localização dos objetos do medidor no gráfico |
GaugeSetCaseParameters | Define os parâmetros do corpo do medidor |
GaugeSetLegendParameters | Define os parâmetros da legenda |
GaugeSetMarkLabelFont | Define a fonte das marcações de graduação |
GaugeSetMarkParameters | Define os parâmetros escalares da graduação |
GaugeSetNeedleParameters | Define os parâmetros da agulha |
GaugeSetRangeParameters | Define os parâmetros do intervalo |
GaugeSetScaleParameters | Define os parâmetros de escala |
Tabela 1. Lista de funções
Vamos considerar cada função a fundo. Elas são representadas na ordem em que recomendamos em chamá-las ao inicializar.
Cria o medidor.
bool GaugeCreate( string name, // Nome do medidor GAUGE_STR &g, // Referência à estrutura do medidor int x, // Travessão horizontal a partir do canto âncora int y, // Travessão vertical a partir do canto âncora int size, // tamanho do medidor string ObjRel, // Nome de um objeto gráfico relativo a sua posição que foi definida ENUM_REL_MODE rel_mode, // Posicionamento relativo ENUM_BASE_CORNER corner, // Canto âncora bool back, // Objetos no fundo uchar scale_transparency, // Transparência da escala uchar needle_transparency // Transparência da agulha );
Parâmetros
name
[Entrada] nome do medidor. Utilizado como um prefixo para os nomes dos objetos gráficos que compõem o medidor.
g
[Saída] Referência à estrutura do medidor.
x
[Entrada] Distância em pixels do canto âncora ao longo do eixo X. No caso de posicionamento relativo - distância do local de origem das coordenadas.
y
[Entrada] Distância em pixels do canto âncora ao longo do eixo Y. No caso de posicionamento relativo - distância do local de origem das coordenadas.
size
[Entrada] Tamanho do medidor. Representado como o diâmetro do corpo.
ObjRel
[Entrada] Nome de outro objeto gráfico relativo a posição que ele foi definido. Permanece pertinente somente se o posicionamento relativo é definido.
rel_mode
[Entrada] Método de posicionamento relativo. Pode ter qualquer valor listado na ENUM_REL_MODE.
corner
[Entrada] Canto Gráfico para ancorar o objeto gráfico. Pode ter qualquer valor listado na ENUM_BASE_CORNER.
back
[Entrada] Objetos no fundo.
scale_transparency
[Entrada] Nível de transparência da escala. Podem ter valores de 0 a 255.
needle_transparency
[Entrada] Nível de transparência da agulha. Podem ter valores de 0 a 255.
Valor de retorno
Retorna true se os objetos da camada de escala e a camada da agulha forem criados. Caso contrário, retorna false.
Define os parâmetros do corpo do medidor.
void GaugeSetCaseParameters( GAUGE_STR &g, // referência à estrutura do medidor ENUM_CASE_STYLE style, // Tipo do corpo color ccol, // Cor do corpo ENUM_CASE_BORDER_STYLE bstyle, // Estilo da borda color bcol, // Cor da borda ENUM_SIZE border_gap_size // Tamanho do espaço entre uma borda e os elementos de escala );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
style
[Entrada] Estilo do corpo. Pode ter qualquer valor listado na ENUM_CASE_STYLE.
ccol
[Entrada] Cor do corpo.
bstyle
[Entrada] Estilo da borda. Pode ter qualquer valor listado na ENUM_CASE_BORDER_STYLE.
bcol
[Entrada] Cor da borda.
gap_size
[Entrada] Área entre a linha interna da borda e o elemento mais próxima da escala ("g" na fig.3). Pode ter qualquer valor listado na ENUM_SIZE.
Define os parâmetros da escala.
void GaugeSetScaleParameters( GAUGE_STR &g, // Referência à estrutura do medidor int range, // Faixa da escala int rotation, // Ângulo de rotação double min, // Valor mínimo (esquerda) double max, // valor máximo (direita) ENUM_MUL_SCALE mul, // Multiplicador das marcações da escala ENUM_SCALE_STYLE style, // Estilo da escala color col, // Cor da escala bool display_arc // Exibição da linha da escala );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
range
[Entrada] Faixa da escala. Definido como um ângulo contido por dois vetores de raio de marcações extremas de escala. Podem ter valores de 30-320 graus (Fig.5).
rotation
[Entrada] Ângulo de rotação da escala (Fig.6).
min
[Entrada] Valor mínimo da escala em caso de atribuição de um número direto.
max
[Entrada] O valor máximo da escala em caso de atribuição de um número direto.
mul
[Entrada] Multiplicador da marcações da escala. Pode ter qualquer valor listado na ENUM_MUL_SCALE.
style
[Entrada] Estilo da escala. Pode ter qualquer valor listado na ENUM_SCALE_STYLE.
col
[Entrada] Cores da escala.
display_arc=false
[Entrada] Exibe a linha de escala.
Define as marcações da escala.
void GaugeSetMarkParameters( GAUGE_STR &g, // Referência à estrutura do medidor ENUM_MARK_STYLE style, // Estilo de marcas da escala ENUM_SIZE size, // Tamanho das marcas double major_tmi, // Intervalo da marcação principal int middle_tmarks, // Número de marcações médias entre as principais int minor_tmarks // Número de marcações menores entre as médias );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
style
[Entrada] Estilo da graduação da escala. Pode ter qualquer valor listado na ENUM_MARK_STYLE.
size
[Entrada] Tamanho da marcação. Pode ter qualquer valor listado na ENUM_SIZE.
major_tmi
[Entrada] Passo da graduação das marcações principais. As principais marcas são acompanhadas por etiquetas com valores relevantes.
middle_tmarks
[Entrada] Número de marcações médias entre as principais. Pode ter qualquer valor positivo. Não há restrições de tamanho. Se definido como 0, as marcações médias não são exibidas.
minor_tmarks
[Entrada] Número de marcações menores entre as médias (ou entre as as principais se as médias não forem apresentadas). Pode ter qualquer valor positivo. Não há restrições de tamanho. Se definido como 0, as marcações menores não são exibidas.
Define a fonte da marcações de graduação.
void GaugeSetMarkLabelFont( GAUGE_STR &g, // Referência à estrutura do medidor ENUM_SIZE font_size, // Tamanho da fonte string font, // Fonte bool italic, // Itálico bool bold, // Negrito color col // Cor );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
font_size
[Entrada] Tamanho da fonte da marcações de graduação. Pode ter qualquer valor listado na ENUM_SIZE.
font
[Entrada] Fonte.
italic
[Entrada] Itálico.
bold
[Entrada] Negrito.
col
[Entrada] Cor da fonte.
Define os parâmetros da legenda.
void GaugeSetLegendParameters( GAUGE_STR &g, // referência à estrutura do medidor ENUM_GAUGE_LEGEND gl, // Tipo de legenda bool enable, // Exibe legenda string str, // String (ou um parâmetro complementar) int radius, // Coordenadas - raio double angle, // Coordenadas - ângulo uint font_size, // Tamanho da fonte string font, // Fonte bool italic, // Itálico bool bold, // Negrito color col // Cor );
Parâmetros
g
[Saída] Referência à estrutura do medidor
gl
[Entrada] Tipo da legenda. Pode ter qualquer valor listado na ENUM_GAUGE_LEGEND.
enable
[Entrada] Mostra a legenda.
str
[Entrada] Esta é uma string exibida para as legendas do tipo LEGEND_DESCRIPTION ou LEGEND_UNITS. Este parâmetro é ignorado para uma legenda do tipo LEGEND_MUL. Número de casas decimais para uma legenda do tipo LEGEND_VALUE. Podem ter valores de "0" a "8". Quaisquer outros valores são percebidos como "0". Por exemplo, a string "2" significa duas casas decimais. A cadeia "hello" significa 0 casas decimais.
radius
[Entrada] Raio. Distância do centro do medidor para o centro da legenda em unidades condicionais (fig. 9).
angle
[Entrada] Coordenadas angulares. O seu valor é igual ao ângulo entre a linha que vem de cima e na vertical do centro do medidor, e um segmento imaginário, que liga o centro do medidor e o centro da legenda (Fig.9).
font_size
[Entrada] Tamanho da fonte da legenda.
font
[Entrada] Fonte.
italic
[Entrada] Itálico.
bold
[Entrada] Negrito.
col
[Entrada] Cor da fonte.
Define os parâmetros da faixa destacada.
void GaugeSetRangeParameters( GAUGE_STR &g, // Referência à estrutura do medidor int index, // Índice do intervalo bool enable, // Intervalo de exibição double start, // Valor inicial double end, // Valor final color col // Cor );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
index
[Entrada] ïndice do intervalo Podem ter valores de 0 a 3.
enable
[Entrada] Faixa de exibição.
start
[Entrada] Valor inicial.
end
[Entrada] Valor final.
col
[Entrada] Cor para realçar o intervalo.
Define os parâmetros da agulha.
void GaugeSetNeedleParameters( GAUGE_STR &g, // Referência à estrutura do medidor ENUM_NCENTER_STYLE ncenter_style, // Estilo do centro da agulha color ncenter_col, // Cor do entro da agulha color needle_col, // Cor da agulha ENUM_NEEDLE_FILL needle_fill // Método de preenchimento da área da agulha );
Parâmetros
g
[Saída] Referência à estrutura do medidor.
ncenter_style
[Entrad] Estilo do centro da agulha. Pode ter qualquer valor listado na ENUM_NCENTER_STYLE.
ncenter_col
[Entrada] Cor do centro da agulha.
needle_col
[Entrada] Cor da agulha.
needle_fill
[Entrada] Método de preenchimento da área da agulha. Pode ter qualquer valor listado na ENUM_NEEDLE_FILL.
Redesenha o medidor. A função tem de ser chamado depois de alterar quaisquer parâmetros para aplicar essas alterações.
void GaugeRedraw( GAUGE_STR &g // Referência à estrutura do medidor );
Parâmetros
g
[Entrada] Referência à estrutura do medidor.
Atualiza a posição da agulha e o valor apresentado.
void GaugeNewValue( GAUGE_STR &g, // Referência à estrutura do medidor double v // Valor da variável );
Parâmetros
g
[Entrada] Referência à estrutura do medidor.
v
[Entrada] O valor atual da variável.
Exclui os objetos gráficos que compõem o medidor. Chame a função do manipulador OnDeinit().
void GaugeDelete( GAUGE_STR &g // Referência à estrutura do medidor );
Parâmetros
g
[Entrada] Referência à estrutura do medidor.
Calcula as coordenadas dos objetos do medidor. Se o posicionamento relativo está desativado, ele sempre retornará as mesmas coordenadas. Caso contrário, as coordenadas podem ser diferentes dos valores anteriores se o objeto de referência mudou sua localização ou de tamanho.
bool GaugeCalcLocation( GAUGE_STR& g // Referência à estrutura do medidor );
Parâmetros
g
[Entrada] Referência à estrutura do medidor.
Valor de retorno
Retorna true se os valores das coordenadas diferir das anteriores. Caso contrário, retorna false. Se a função retorna true, chama a função GaugeRelocation() para aplicar os parâmetros calculados.
Localiza os objetos gráficos que compõem o medidor no local especificado do gráfico. Necessário chamar se o posicionamento relativo é definido e a função GaugeCalcLocation() retornar true.
void GaugeRelocation( GAUGE_STR &g // Referência à estrutura do medidor );
Parâmetros
g
[Entrada] Referência à estrutura do medidor.
4. Enumerações
A Tabela 2 lista as enumerações usadas como parâmetros de função.
Enumeração | Descrição |
---|---|
ENUM_CASE_BORDER_STYLE | Estilo de borda |
ENUM_CASE_STYLE | Estilo do corpo |
ENUM_GAUGE_LEGEND | Tipo de legenda |
ENUM_MARK_STYLE | Estilo de graduação da escala |
ENUM_MUL_SCALE | Multiplicador das marcações de graduação de escala |
ENUM_NCENTER_STYLE | Estilo do centro da agulha |
ENUM_NEEDLE_FILL | Método de preenchimento da área da agulha |
ENUM_REL_MODE | Método de posicionamento relativo |
ENUM_SCALE_STYLE | Estilo da escala |
ENUM_SIZE | Tamanho |
Tabela 2. Lista de enumerações
4.1. ENUM_CASE_BORDER_STYLE
Estilo de borda. Os valores são listados na tabela 3.
Identificador | Descrição |
---|---|
CASE_BORDER_NONE | Sem borda |
CASE_BORDER_THIN | Borda fina |
CASE_BORDER_THICK | Borda espessa |
Tabela 3. Valores de ENUM_CASE_BORDER_STYLE
4.2. ENUM_CASE_STYLE
Estilo do corpo. Os valores estão listados na tabela 4.
Identificador | Descrição |
---|---|
CASE_ROUND | Corpo Circular |
CASE_SECTOR | Tipo do setor do corpo |
Tabela 4. Valores de ENUM_CASE_STYLE
4.3. ENUM_GAUGE_LEGEND
Tipo de Legenda. Os valores estão listados na tabela 5.
Identificador | Descrição |
---|---|
LEGEND_DESCRIPTION | Descrição do medidor |
LEGEND_UNITS | Unidades de medida |
LEGEND_MUL | Multiplicador das marcações de escala |
LEGEND_VALUE | O valor atual da variável |
Tabela 5. Valores de ENUM_GAUGE_LEGEND
4.4. ENUM_MARK_STYLE
Estilo de graduação da escala. Os valores estão listados na tabela 6.
Identificador | Descrição |
---|---|
MARKS_INNER | Marcações de alinhando pela borda interna |
MARKS_MIDDLE | Marcações de alinhando pelo centro |
MARKS_OUTER | Marcações de alinhando pela borda externa |
Tabela 6. Valores de ENUM_MARK_STYLE
4.5. ENUM_MUL_SCALE
Multiplicador das marcações de graduação da escala. Os valores estão listados na tabela 7.
Identificador | Significado | Exibição |
---|---|---|
MUL_10000 | 10000 | х10k |
MUL_1000 | 1000 | х1k |
MUL_100 | 100 | х100 |
MUL_10 | 10 | х10 |
MUL_1 | 1 | Sem exibição |
MUL_01 | 0.1 | /10 |
MUL_001 | 0.01 | /100 |
MUL_0001 | 0.001 | /1k |
MUL_00001 | 0.0001 | /10k |
Tabela 7. Valores de ENUM_MUL_SCALE
4.6. ENUM_NCENTER_STYLE
Estilo do centro da agulha. Os valores estão listados na Tabela 8.
Identificador | Descrição |
---|---|
NDCS_NONE | Não exibe o centro da agulha |
NDCS_SMALL | Exibição pequena |
NDCS_LARGE | Exibição grande |
Tabela 8. Valores de ENUM_NCENTER_STYLE
4.7. ENUM_NEEDLE_FILL
Método de preenchimento da área da agulha. Os valores estão listados na Tabela 9.
Identificador | Descrição |
---|---|
NEEDLE_FILL | Preenche a agulha sem suavização das bordas |
NEEDLE_FILL_AA | Preencha a agulha com suavização das bordas |
NEEDLE_NOFILL_AA | Não preenche a agulha, mas aplica a suavização de bordas |
Tabela 9. Valores de ENUM_NEEDLE_FILL
4.8. ENUM_REL_MODE
Método de posicionamento relativo. Os valores estão listados na tabela 10.
Identificador | Descrição |
---|---|
RELATIVE_MODE_NONE | O posicionamento relativo é desativado |
RELATIVE_MODE_HOR | Horizontalmente |
RELATIVE_MODE_VERT | Verticalmente |
RELATIVE_MODE_DIAG | Diagonalmente |
Tabela 10. Valores de ENUM_REL_MODE
4.9. ENUM_SCALE_STYLE
Estilo da escala Os valores estão listados na tabela 11.
Identificador | Descrição |
---|---|
SCALE_INNER | Etiquetas de graduação dentro da escala |
SCALE_OUTER | Etiquetas de graduação fora da escala |
Tabela 11. Valores de ENUM_SCALE_STYLE
4.10. ENUM_SIZE
Tamanho. Os valores estão listados na tabela 12.
Identificador | Descrição |
---|---|
SIZE_SMALL | Pequeno |
SIZE_MIDDLE | Médio |
SIZE_LARGE | Grande |
Tabela 12. Valores de ENUM_SIZE
5. Substituição de Macro
Coeficientes para os tamanhos:
#define DIAM_TO_NDCSL_RATIO 5 // Diâmetro do centro da agulha (pequeno) como percentual do diâmetro do corpo #define DIAM_TO_NDCSB_RATIO 7.5 // Diâmetro do centro da agulha (grande) como percentagem do diâmetro do corpo //--- #define DIAM_TO_BD_SIZE_S 2 // Largura da borda (pequeno) como percentual do diâmetro do corpo #define DIAM_TO_BD_SIZE_B 5 // Largura da borda (grande) como percentual do diâmetro do corpo //--- #define DIAM_TO_BD_GAP_S 2.0 // Espaço da borda do corpo de elementos interiores do medidor (pequena) como percentagem do diâmetro do corpo #define DIAM_TO_BD_GAP_M 3.0 // Espaço da borda do corpo de elementos interiores do medidor (médio ), como percentagem do diâmetro do corpo #define DIAM_TO_BD_GAP_L 7.0 // Espaço da borda do corpo de elementos interiores do medidor (grande) como percentagem do diâmetro do corpo //--- #define DIAM_TO_MSZ_MJ_S 3.3 // Tamanho da graduação da escala principal (pequena) em percentagem do diâmetro do corpo #define DIAM_TO_MSZ_MD_S 2.3 // Tamanho da graduação da escala média (pequena) como percentagem do diâmetro do corpo #define DIAM_TO_MSZ_MN_S 1.3 // Tamanho da graduação da escala menor (pequena) como percentual do diâmetro do corpo //--- #define DIAM_TO_MSZ_MJ_M 6.5 // Tamanho da graduação da escala principal (média) como percentual do diâmetro do corpo #define DIAM_TO_MSZ_MD_M 4.8 // Tamanho da graduação da escala média (média) como percentual do diâmetro do corpo #define DIAM_TO_MSZ_MN_M 3.0 // Tamanho da graduação da escala menor (média) como percentual do diâmetro do corpo //--- #define DIAM_TO_MSZ_MJ_L 10.0 // Tamanho da graduação da escala principal (grande) como percentagem do diâmetro do corpo #define DIAM_TO_MSZ_MD_L 7.5 // Tamanho da graduação da escala média (grande) como percentagem do diâmetro do corpo #define DIAM_TO_MSZ_MN_L 5.0 // Tamanho da graduação da escala menor (grande) como percentual do diâmetro do corpo //--- #define DIAM_TO_MFONT_SZ_S 4 // Tamanho da fonte da graduação da escala das marcações (pequenas) como percentagem do diâmetro do corpo #define DIAM_TO_MFONT_SZ_M 6.5 // Tamanho da fonte da graduação da escala das marcações (média) como percentagem do diâmetro do corpo #define DIAM_TO_MFONT_SZ_L 10 // Tamanho da fonte da graduação da escala das marcações (grande) como percentagem do diâmetro do corpo
Cores padrão:
#define DEF_COL_SCALE clrBlack #define DEF_COL_MARK_FONT clrBlack #define DEF_COL_CASE clrMintCream #define DEF_COL_BORDER clrLightSteelBlue #define DEF_COL_LAB clrDarkGray #define DEF_COL_NCENTER clrLightSteelBlue #define DEF_COL_NEEDLE clrDimGray
6. Modificando a Classe CCanvas
6.1. Desenhando um Segmento Usando o Algoritmo de Anti-Aliasing
O método LineAA permite desenhar um segmento usando o algoritmo de suavização. Mas um problema aparece quando tiramos as marcações da escala circular. Quando nós convertemos as coordenadas dos pontos iniciais e finais do segmento do sistema de coordenada polar para a cartesiana, tivemos números fracionários que arredondamos para um número inteiro. Consequentemente as marcas pareceram curvadas, o qual é mostrado na Fig.11 (b).
É por isso que nós adicionamos o método LineAA2, que difere de LineAA pelo fato do tipo dos parâmetros de entrada x1, y1, x2, y2 terem alterados para o dobro. Assim, podemos entregar valores fracionários de coordenadas e se livrar do problema mencionado, que é vividamente visto na figura 11(c).
//+------------------------------------------------------------------+ //| Desenha a Linha com suavização (com estilo) v.2 | //+------------------------------------------------------------------+ void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style) { //--- Linha está fora dos limites da imagem if((x1<0 && x2<0) || (y1<0 && y2<0)) return; if(x1>=m_width && x2>=m_width) return; if(y1>=m_height && y2>=m_height) return; //--- Verifica if(x1==x2 && y1==y2) { PixelSet(int(x1),int(y1),clr); return; } //--- Definir o estilo de linha if(style!=UINT_MAX) LineStyleSet(style); //--- Cálculos preliminares double dx=x2-x1; double dy=y2-y1; double xy=sqrt(dx*dx+dy*dy); double xx=x1; double yy=y1; uint mask=1<<m_style_idx; //--- Define os pixels dx/=xy; dy/=xy; do { if((m_style&mask)==mask) PixelSetAA(xx,yy,clr); xx+=dx; yy+=dy; mask<<=1; if(mask==0x1000000) mask=1; } while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy)); }
Fig.11 ilustra os exemplos de vários métodos de marcas da escala de desenho:
Fig. 11. Vários métodos de desenhar as marcações da escala (um aumento de 200%)
6.2. Preenchendo uma Área com Arestas Anti-Aliasing
O método Fill é destinado a preencher uma área delimitada por segmentos desenhados sem o uso de algoritmo anti-aliasing. Se nós utilizarmos este método para o preenchimento de uma zona delimitada por segmentos desenhados pelo método LineAA, a área será preenchida de forma incompleta, o que é visto na Fig.12 (a).
Fig.12. Preenchendo uma área com bordas suavizadas (aumento de 200%)
Então, nós adicionamos o método Fill2. A diferença é que ele não preenche a cor de fundo, mas qualquer cor diferente da cor dos segmentos que se ligam a área. Ela permite preencher um meio-tom, que não pode ser feito usando o método Fill. Fig.12 (b) mostra um exemplo.
//+------------------------------------------------------------------+ //| Preenche uma região fechada com cor (v.2) | //+------------------------------------------------------------------+ void CCanvas::Fill2(int x,int y,const uint clr) { //--- Verifica if(x<0 || x>=m_width || y<0 || y>=m_height) return; //--- int index=y*m_width+x; uint old_clr=m_pixels[index]; //--- Verifica se a substituição é necessária if(old_clr==clr) return; //--- Usa um pseudo empilhamento para emular as chamadas recursivas muito aninhadas int stack[]; uint count=1; int idx; int total=ArraySize(m_pixels); //--- Aloca memória para a pilha if(ArrayResize(stack,total)==-1) return; stack[0]=index; m_pixels[index]=clr; for(uint i=0;i<count;i++) { index=stack[i]; x=index%m_width; //--- Ponto adjacente da esquerda idx=index-1; if(x>0 && m_pixels[idx]!=clr) { stack[count]=idx; if(m_pixels[idx]==old_clr) count++; m_pixels[idx]=clr; } //--- Ponto adjacente superior idx=index-m_width; if(idx>=0 && m_pixels[idx]!=clr) { stack[count]=idx; if(m_pixels[idx]==old_clr) count++; m_pixels[idx]=clr; } //--- Ponto adjacente à direita idx=index+1; if(x<m_width-1 && m_pixels[idx]!=clr) { stack[count]=idx; if(m_pixels[idx]==old_clr) count++; m_pixels[idx]=clr; } //--- Ponto adjacente inferior idx=index+m_width; if(idx<total && m_pixels[idx]!=clr) { stack[count]=idx; if(m_pixels[idx]==old_clr) count++; m_pixels[idx]=clr; } } //--- Desalocar memória ArrayFree(stack); }
No entanto, este método também tem desvantagens. Se há um pequeno ângulo agudo, a sua parte permanecerá não preenchida, o qual é mostrado na Fig.12 (c). Então, nós encontramos uma maneira de sair deste problema.
1) Primeiro todo a tela é preenchida (camada de agulha) com a cor significativa para a agulha:
n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));
2) Em seguida, desenhamos a agulha composta por três segmentos usando o método LineAA2:
n.canvas.LineAA2(db_xbuf[0], db_ybuf[0], db_xbuf[1], db_ybuf[1], 0); n.canvas.LineAA2(db_xbuf[1], db_ybuf[1], db_xbuf[2], db_ybuf[2], 0); n.canvas.LineAA2(db_xbuf[2], db_ybuf[2], db_xbuf[0], db_ybuf[0], 0);
3) Após isso, preencher a área em torno da agulha com a cor transparente usando o método Fill2:
n.canvas.Fill2(10, 10, 0);
Este método não é o melhor, mas permite desenhar a agulha adequada.
Fig.13. Agulhas preenchidas usando métodos diferentes
Fig.13 descreve agulhas cheias usando métodos diferentes.
- a) A agulha composta por três segmentos desenhados utilizando o método LineAA2 e preenchido com o método Fill2.
- b) A agulha desenhada através do método FillTriangle.
- c) A agulha não preenchida composta por três segmentos desenhados usando o método LineAA2.
Como podemos ver, a agulha mostrada na fig.13 (b) é escarpada e tem pequenos desvios a partir dos ângulos divisíveis por 90 graus. Além disso, podemos ver que a agulha é deslocada do centro, que é causada pelo arredondamento dos valores das coordenadas quando convertê-mos o sistema de coordenadas de polar para cartesiana. Mas, ao mesmo tempo, este método é o mais prático no contexto da intensidade de recursos (voltaremos a este assunto mais tarde). A agulha mostrada na fig.13 (c) é uma compensação dos dois métodos descritos acima. Ele é composto por três segmentos desenhados utilizando o método LineAA2 mas sem preenchimento da área.
7. Exemplos de Aplicação
Vamos rever a aplicação da biblioteca do medidor através de vários exemplos.
Vamos começar com o mais simples. O exemplo a seguir demonstra o conjunto básico para adicionar o medidor a um EA ou um indicador.
//+------------------------------------------------------------------+ //| profit_gauge_indicator.mq5 | //| Copyright 2015, Decanium | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Decanium" #property version "1.00" #property indicator_plots 0 #property indicator_chart_window #include <Gauge\gauge_graph.mqh> input string inp_gauge_name="gg01"; // Nome do Indicador input int inp_x = 40; // Deslocamento Horizontal input int inp_y = 40; // Deslocamento Vertical input int inp_size=300; // Tamanho do Indicador input string inp_ObjRel=""; // Nome do indicador de base em caso de posicionamento relativo input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Modo de posicionamento relativo input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Canto âncora input bool inp_back=false; // Indicador está em segundo plano input uchar inp_scale_transparency=0; // Nível de transparência da escala, 0..255 input uchar inp_needle_transparency=0; // Nível de transparência da agulha, 0..255 // --- Declaração da estrutura do medidor GAUGE_STR g0; //+------------------------------------------------------------------+ //| Função de inicialização do indicador personalizado | //+------------------------------------------------------------------+ int OnInit() { //--- Criando o medidor if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode, inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Desenho do medidor GaugeRedraw(g0); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Função de desinicialização do indicador personalizado | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Exclusão do medidor GaugeDelete(g0); ChartRedraw(); } //+------------------------------------------------------------------+ //| Função de iteração do indicador personalizado | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- Atualização das leituras double profit=AccountInfoDouble(ACCOUNT_PROFIT); GaugeNewValue(g0,profit); //--- return(rates_total); } //+------------------------------------------------------------------+ //| Função ChartEvent | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- if(id==CHARTEVENT_CHART_CHANGE) { if(GaugeCalcLocation(g0)==true) GaugeRelocation(g0); } } //+------------------------------------------------------------------+
Primeiro, precisamos declarar a estrutura do medidor. Em seguida, continuamos com a função de inicialização onde criamos o medidor usando a função GaugeCreate() e chamamos a função de desenho - GaugeRedraw(). GaugeNewValue() pode ser usado para atualizar as leituras. Neste exemplo, ele é chamado do manipulador OnCalculate().
O medidor vai parecer como mostrado na fig.14.
Fig.14. Aparência padrão do medidor
Agora vamos adicionar a capacidade de definir o intervalo de escala e o ângulo de rotação. Ele irá adicionar dois parâmetros para a lista de entradas.
input int inp_scale_range=270; // Faixa de escala, 30..320 graus input int inp_rotation=45; // Escala da rotação, 0..359 graus
Agora nós estendemos o código de inicialização com a chamada da função para definir os parâmetros de escala.
//--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);
Nós também iremos definir novos parâmetros de entrada complementares:
- nova valores máximos e mínimos (-200 e 400, respectivamente)
- multiplicador de marcas de graduação da escala (MUL_1)
- estilo da escala (SCALE_INNER - etiquetas de graduação internas)
- cor das etiquetas (clrBlack)
À medida que forem alterados os valores extremos da escala, é desejável corrigir um passo da marcação principal. O melhor valor é de 100, já que exclui a abundância do texto. Naquele nós vamos colocar uma marcação média entre os dois principais e os quatro menores entre as duas marcas médias. Assim teremos um passo mínimo entre as marcas iguais a 10.
GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);
Agora vamos destacar dois intervalos de dados. O intervalo com índice 0, que começa com 200 e termina com 400, será destacado com a cor clrLimeGreen. O intervalo com índice 1, que começa com -100 e termina com -200, será destacado com a cor clrCoral.
//--- Intervalos de destaque na escala GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen); GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);
Agora nós vamos definir as legendas. Nós determinamos a descrição do medidor, unidades de medida e o valor atual com uma casa decimal. Vamos revisá-las uma por uma.
Descrição do medidor
GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);
Exibição do texto "Profit", raio é 3, ângulo é 0, fonte é 14 unidades condicionais.
Unidades de medida:
GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);
Exibição do texto "USD", raio é 8, ângulo é 215, fonte é 10 unidades condicionais.
Valor atual:
GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);
Aqui o texto "1", significa a forma de exibição (uma casa decimal). Coordenadas: raio é 4, o ângulo é de 255. Tamanho da fonte é de 20 unidades condicionais.
Assim, depois de ter realizado algumas configurações adicionais, o medidor vai parecer como mostrado na fig.15.
Fig.15. Aparência do medidor após ajuste adicional
Agora nós vamos rever o exemplo mais complicado, ou seja, o Painel indicador. Ele é mostrado na fig.1. O indicador mostra o lucro atual, spread, margem livre como percentuais e atuais valores de ATR, Índice de Força e RSI.
Primeiro vamos declarar o array de estrutura do medidor.
//--- Declaração do array de estrutura do medidor GAUGE_STR gg[6];
Então, vamos criar e ajustar o medidor.
O indicador do nível da margem será colocado no canto inferior esquerdo. Ela terá coordenadas absolutas, e todos os outros indicadores serão localizados em relação a este indicador ou a um vizinho.
//--- Construindo o medidor gg00, nível de margem if(GaugeCreate("gg00",gg[0],5,-90,240,"",RELATIVE_MODE_NONE, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[0],120,35,800,2000,MUL_100,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[0],MARKS_INNER,SIZE_MIDDLE,200,1,4); GaugeSetMarkLabelFont(gg[0],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Intervalos de destaque na escala GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen); GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[0],LEGEND_DESCRIPTION,true,"Margin lvl",4,15,12,"Arial",false,false); GaugeSetLegendParameters(gg[0],LEGEND_VALUE,true,"0",3,80,16,"Arial",true,false); GaugeSetLegendParameters(gg[0],LEGEND_MUL,true,"",4,55,8,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
Continuamos organizando a linha inferior. O próximo é o indicador de lucro atual.
//--- Construindo o medidor gg01, o lucro atual if(GaugeCreate("gg01",gg[1],-80,20,320,"gg00",RELATIVE_MODE_HOR, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[1],200,0,-400,400,MUL_1,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[1],MARKS_INNER,SIZE_MIDDLE,100,1,4); GaugeSetMarkLabelFont(gg[1],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Intervalos de destaque na escala GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen); GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[1],LEGEND_DESCRIPTION,true,"Profit",3,0,16,"Arial",false,false); GaugeSetLegendParameters(gg[1],LEGEND_UNITS,true,"USD",3,-90,10,"Arial",true,false); GaugeSetLegendParameters(gg[1],LEGEND_VALUE,true,"1",3,90,12,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
O indicador de spread fecha a linha de fundo.
//--- Construção do medidor gg02, spread if(GaugeCreate("gg02",gg[2],-80,-20,240,"gg01",RELATIVE_MODE_HOR, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[2],120,-35,60,0,MUL_1,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[2],MARKS_INNER,SIZE_MIDDLE,10,1,4); GaugeSetMarkLabelFont(gg[2],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Intervalos de destaque na escala GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen); GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[2],LEGEND_DESCRIPTION,true,"Spread",4,-15,14,"Arial",false,false); GaugeSetLegendParameters(gg[2],LEGEND_VALUE,true,"0",3,-80,16,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
O indicador ATR (a esquerda na fila superior) é colocado relativamente ao indicador de margem livre.
// --- Construção do medidor gg03, ATR if(GaugeCreate("gg03",gg[3],30,0,180,"gg00",RELATIVE_MODE_VERT, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[3],270,45,0.001,0.004,MUL_00001,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[3],MARKS_INNER,SIZE_LARGE,0.001,9,3); GaugeSetMarkLabelFont(gg[3],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Intervalos de destaque na escala GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[3],LEGEND_DESCRIPTION,true,"ATR",7,-140,26,"Arial",false,false); //GaugeSetLegendParameters(gg[3],LEGEND_UNITS,true,"USD",8,180,5,"Arial",true,false); GaugeSetLegendParameters(gg[3],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false); GaugeSetLegendParameters(gg[3],LEGEND_MUL,true,"",2,0,20,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
O indicador RSI é colocado relativamente ao indicador de spread (acima).
//--- Construção do medidor gg04, RSI if(GaugeCreate("gg04",gg[4],-30,0,180,"gg02",RELATIVE_MODE_VERT, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[4],270,45,0,100,MUL_10,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[4],MARKS_INNER,SIZE_LARGE,10,1,4); GaugeSetMarkLabelFont(gg[4],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[4],LEGEND_DESCRIPTION,true,"RSI",7,-140,26,"Arial",false,false); GaugeSetLegendParameters(gg[4],LEGEND_VALUE,true,"2",2,180,16,"Arial",true,false); GaugeSetLegendParameters(gg[4],LEGEND_MUL,true,"",2,0,20,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
O indicador Índice de Força é colocado acima do indicador de lucro atual.
//--- Constuindo medidor gg05, Force if(GaugeCreate("gg05",gg[5],-10,60,180,"gg03",RELATIVE_MODE_HOR, CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false) return(INIT_FAILED); //--- Definindo os parâmetros do corpo GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- Parâmetros de ajuste da escala e marcas na escala GaugeSetScaleParameters(gg[5],270,45,-4,4,MUL_1,SCALE_INNER,clrBlack); GaugeSetMarkParameters(gg[5],MARKS_INNER,SIZE_LARGE,1,1,4); GaugeSetMarkLabelFont(gg[5],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT); //--- Intervalos de destaque na escala GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen); GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson); //--- Configuração dos rótulos de texto GaugeSetLegendParameters(gg[5],LEGEND_DESCRIPTION,true,"Force",7,-140,20,"Arial",false,false); GaugeSetLegendParameters(gg[5],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false); GaugeSetLegendParameters(gg[5],LEGEND_MUL,true,"",3,0,10,"Arial",true,false); //--- Configuração dos Parâmetros da agulha GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
Medidores podem ser desenhados de maneira cíclica.
//--- Desenhando Medidores for(int i=0; i<6;i++) { GaugeRedraw(gg[i]); GaugeNewValue(gg[i],0); }
Quando o evento OnCalculate() ocorre, nós recalculamos os valores atuais e chamamos a função GaugeNewValue() para cada indicador.
//--- Atualização das leituras //--- Spread GaugeNewValue(gg[2],spread[rates_total-1]); //--- Lucro atual double profit=AccountInfoDouble(ACCOUNT_PROFIT); GaugeNewValue(gg[1],profit); //--- Nível da margem double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); GaugeNewValue(gg[0],margin_level); //--- O indicador de ATR calculated=BarsCalculated(handle_ATR); if(calculated>0) { double ival[1]; if(CopyBuffer(handle_ATR,0,0,1,ival)<0) Print("ATR CopyBuffer error"); else GaugeNewValue(gg[3],ival[0]); } //--- O indicador RSI calculated=BarsCalculated(handle_RSI); if(calculated>0) { double ival[1]; if(CopyBuffer(handle_RSI,0,0,1,ival)<0) Print("RSI CopyBuffer error"); else GaugeNewValue(gg[4],ival[0]); } //--- O indicador índice de força calculated=BarsCalculated(handle_Force); if(calculated>0) { double ival[1]; if(CopyBuffer(handle_Force,0,0,1,ival)<0) Print("Force Index CopyBuffer error"); else GaugeNewValue(gg[5],ival[0]); }
Por favor, note que não há nenhum ponto para chamar a função GaugeRelocation() do evento OnChartEvent() no exemplo dado. Embora o posicionamento relativo é utilizado aqui, não precisamos recalcular as coordenadas dos medidores se a posição ou o tamanho de um deles mudoar, já que os medidores são inicializados todos de uma vez.
8. Avaliação da Intensidade dos Recursos
A camada de agulha está totalmente redesenhando sempre que ocorrem uma atualização nas leituras. Pode acontecer com bastante frequência, mesmo várias vezes por segundo em alguns casos. É por isso que o problema da intensidade de recursos de desenho da agulha é bastante aguda. Vamos escrever um pequeno script para avaliar a sobrecarga do CPU para desenhar a agulha usando vários métodos de preenchimento da área.
//+------------------------------------------------------------------+ //| test_fill.mq5 | //| Copyright 2015, Decanium | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Decanium" #property version "1.00" #include <Canvas/Canvas2.mqh> CCanvas canvas; //+------------------------------------------------------------------+ //| Script da função de início do programa | //+------------------------------------------------------------------+ void OnStart() { Print("***** start test *****"); //--- string ObjName="test"; ObjectDelete(0,ObjName); canvas.CreateBitmapLabel(ObjName,10,10,400,400,COLOR_FORMAT_ARGB_NORMALIZE); //--- int x[3]={200,185,215}; int y[3]={70, 250,250}; int cycles=1000; uint col=ColorToARGB(clrRed,255); uint c1,c2; //--- Testando o preenchimento da área de arestas suavizadas canvas.Erase(); c1=GetTickCount(); for(int i=0; i<cycles; i++) { canvas.Fill(10, 10, col); canvas.LineAA2(x[0], y[0], x[1], y[1], 0); canvas.LineAA2(x[1], y[1], x[2], y[2], 0); canvas.LineAA2(x[2], y[2], x[0], y[0], 0); canvas.Fill2(10, 10, 0); } c2=GetTickCount(); canvas.Update(true); Print("Filled AA: ",c2-c1," ms, ",cycles," cycles, ", DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle"); //--- Testando o contorno anti-aliasing sem preencher canvas.Erase(); c1=GetTickCount(); for(int i=0; i<cycles; i++) { canvas.LineAA2(x[0], y[0], x[1], y[1], col); canvas.LineAA2(x[1], y[1], x[2], y[2], col); canvas.LineAA2(x[2], y[2], x[0], y[0], col); } c2=GetTickCount(); canvas.Update(true); Print("Not filled AA: ",c2-c1," ms, ",cycles," cycles, ", DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle"); //--- Testando o preenchimento da área sem suavização canvas.Erase(); c1=GetTickCount(); for(int i=0; i<cycles; i++) { canvas.FillTriangle(x[0],y[0],x[1],y[1],x[2],y[2],col); canvas.LineAA2(x[0], y[0], (x[1]+x[2])/2, y[1], col); } c2=GetTickCount(); canvas.Update(true); Print("Filled: ",c2-c1," ms, ",cycles," cycles, ", DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle"); } //+------------------------------------------------------------------+
O script lança cada método de desenho da agulha 1.000 vezes em um ciclo e mede o tempo gasto para este processo em milissegundos.
Fig.16. Os resultados do teste de intensidade de recursos
Como você pode ver pelos resultados, a agulha preenchido com bordas suavizadas é desenhada centenas de vezes mais do que a agulha cheia sem anti-aliasing e dezenas de vezes mais do que apenas uma linha de contorno suavizada sem preencher. Neste caso, a beleza realmente tem o seu preço.
Conclusão
Neste artigo, nós analisamos o conjunto de funções para desenhar medidores. O principal alvo da criação da biblioteca foi a simplicidade de adicionar medidores para um EA ou um indicador sem se aprofundar nos detalhes do desenho e geometria. No entanto, cabe a você decidir se atingimos essa meta.
Especial atenção deve ser dada à intensidade dos recursos. Cálculos demorados no manipulador OnCalculate() podem causar a suspensão do terminal. Então, nós recomendamos a aplicação do método de desenhar a agulha suavizada sem preenchimento.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1699
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso