Dibujo de indicadores de aguja usando la clase CCanvas
Serhii Shevchuk | 1 octubre, 2015
Contenido
- Introducción
- 1. Coordenadas y anclaje
- 2. Elementos del instrumento indicador
- 3. Funciones
- 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. Enumeraciones
- 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. Macro sustituciones
- 6. Últimos retoques de la clase CCanvas
- 7. Ejemplos de aplicación
- 8. Evaluación del consumo de recursos
- Conclusión
Introducción
Todo comenzó cuando conocí la clase CCanvas por primera vez. Cuando la cosa llegó a la práctica, me surgió la idea de diseñar un instrumento indicador. Los primeros resultados eran primitivos, pero durante el proceso de mejoras los instrumentos se completaban con nuevos elementos y adquirían la apariencia más agradable. Como resultado, he obtenido una pequeña biblioteca que ahora permite añadir fácilmente un instrumento indicador a cualquier indicador o Asesor Experto (EA). En este artículo vamos a considerar la estructura del instrumento indicador, conoceremos las funciones necesarias para el dibujo y configuración de su aspecto visual, también evaluaremos el consumo de recursos.
Fig. 1. Instrumentos indicadores
1. Coordenadas y anclaje
Hay dos tipos de posicionar un instrumento sobre el gráfico: absoluto y relativo.
En caso del posicionamiento absoluto, las coordenadas representan las distancias en píxeles desde la esquina de anclaje por los ejes X y Y.
En caso del posicionamiento relativo, se crea el principio local de las coordenadas según el tipo especificado del posicionamiento relativo. Cuando se selecciona el tipo vertical, el principio estará ubicado debajo o encima del objeto de referencia (si está seleccionado la esquina de anclaje superior o inferior, respectivamente). Cuando se selecciona el tipo horizontal, el principio estará a la izquierda o a la derecha en la dirección del alejamiento desde la esquina de anclaje. En este caso, las coordenadas especificadas representan el desplazamiento desde el principio local de las coordenadas. En caso de desplazamientos positivos, el objeto va a alejarse del objeto de referencia. En caso de los desplazamientos negativos, va a cubrirlo.
El objeto de referencia puede ser representado sólo por el objeto de otro instrumento indicador. Es importante que ambos objetos tengan la misma esquina de anclaje.
En la imagen 2 se muestra un ejemplo de la posición relativa.
Fig. 2. Posicionamiento relativo de instrumentos indicadores
Vamos a analizar las configuraciones de cada instrumento:
- Instrumento "gg01": posicionamiento relativo desactivado. Desplazamiento horizontal — 40, desplazamiento vertical — 40.
- Instrumento "gg02": posicionamiento relativo — horizontal, objeto de referencia — "gg01". Desplazamiento desde el principio local de coordenadas (punto A) por la horizontal — 15, desplazamiento por la vertical — 0.
- Instrumento "gg03": posicionamiento relativo — vertical, objeto de referencia — "gg01". Desplazamiento desde el principio local de coordenadas (punto B) por la horizontal — 0, desplazamiento por la vertical — 15.
- Instrumento "gg04": posicionamiento relativo — vertical, objeto de referencia — "gg02". Desplazamiento desde el principio local de coordenadas (punto A) por la horizontal — 50, desplazamiento por la vertical — 15.
El posicionamiento relativo facilita la configuración de los parámetros de entrada si en el gráfico se encuentran varios indicadores que contienen el instrumento indicador. Si el tamaño de uno de ellos cambia, las coordenadas de los demás serán recalculados automáticamente.
El tipo y las coordenadas se establecen a través de la función GaugeCreate().
2. Elementos del instrumento indicador
El instrumento indicador se compone de dos objetos gráficos. Uno de ellos se llama la capa de la escala, el otro se llama la capa de la aguja. Ambos objetos gráficos tienen las mismas coordenadas, la capa de la aguja se encuentra por encima de la capa de la escala. El nombre del instrumento que se establece en los parámetros de entrada sirve de prefijo para los nombres de ambos objetos. Por ejemplo, si el nombre de instrumento es "Gauge01", la capa de la escala tendrá el nombre "Gauge01_s", y la capa de la aguja — "Gauge01_n".
En la figura 3 se muestra la estructura del instrumento indicador.
Fig. 3. Estructura del instrumento indicador
La capa de la escala contiene los elementos siguientes:
- borde (1)
- marcas de graduación de la escala (5, 6, 7)
- etiquetas de graduaciones de la escala (4)
- rangos señalados (2, 12)
- leyendas (3, 10, 11)
Las leyendas se distinguen por su finalidad:
- descripción del instrumento indicador (3)
- unidades de medición (11)
- valor actual (10)
- multiplicador de las marcas de la escala (omitido)
Las marcas de graduación se dividen en:
- principales (7)
- medianas (5)
- pequeñas (6)
Sólo las marcas principales tienen las etiquetas. El paso de las marcas se establece con un valor numérico. El paso de las marcas medianas se calcula basándose en la cantidad determinada de las marcas medianas entre las principales. El paso de las marcas pequeñas se calcula basándose en la cantidad determinada de las marcas pequeñas entre las medianas. Las marcas de graduación medianas y pequeñas pueden ser omitidas.
La capa de la aguja contiene los elementos siguientes:
- aguja (8)
- centro de la aguja (9)
En la figura 3 se muestran los tamaños de algunos elementos del instrumento indicador:
- d — tamaño del instrumento que corresponde al diámetro de la línea exterior del contorno del instrumento
- b — tamaño del borde
- g — tamaño del espacio entre el borde y elementos de la escala
- c — tamaño del centro de la aguja.
Importante. El diámetro del instrumento es el único tamaño que se establece en píxeles (en la fig. 3 es el tamaño d). Los demás tamaños de los elementos y fuentes se indican en unidades condicionales y su valor se calcula en porcentaje del diámetro. Ha sido hecho para facilitar el escalamiento. Será suficiente cambiar el diámetro y todos los demás tamaños serán recalculados proporcionalmente. Los coeficientes para el cálculo se listan en la sección “Macro sustituciones” y pueden ser sustituidos según el deseo del usuario.
La forma del cuerpo de un instrumento indicador puede ser de dos tipos: círculo y sector. La forma de sector es más conveniente para el uso si el ángulo del rango de la escala es inferior a 180 grados.
Fig. 4. Forma del instrumento indicador
En la imagen 4 se muestra un instrumento redondo (a) y dos instrumentos de sector (b, c). Para indicar la forma necesaria, se utiliza la función GaugeSetCaseParameters().
Es el elementos más importante en un instrumento indicador. La legibilidad de los datos depende de su apariencia. La escala no debe parecer sobrecargada, pero al mismo tiempo tiene que ser bastante informativa. La selección de los valores extremos de la escala, así como el paso de las marcas de graduación principales, requiere una atención especial. La función GaugeSetScaleParameters() permite configurar el rango de la escala, su rotación y los valores de puntos extremos (máximo y mínimo). El mínimo puede estar tanto a la izquierda (orden directo), como a la derecha (orden inverso).
El rango de la escala es un ángulo formado por dos radio vectores de los puntos extremos de la escala. El ejemplo se muestra en la figura 5.
Fig. 5. Rango de la escala
La rotación de la escala es el ángulo de desvío de la bisectriz del ángulo del rango de la escala del rayo que sale del centro del instrumento verticalmente arriba. El ejemplo se muestra en la figura 6.
Fig. 6. Ángulo de rotación de la escala
Al combinar el ángulo del rango de la escala y el ángulo de rotación, se puede configurar la apariencia del instrumento de una forma bastante flexible. En la imagen 4(c) hay un instrumento con el rango de 90 grados y rotación de 45.
Los valores mínimos y máximos de la escala son parámetros importantes que deben seleccionarse dependiendo del rango de valores permitidos de la variable a mostrar. La marca cero puede ser omitida por cuestiones de conveniencia. No hay ninguna razón dibujar la escala desde cero si la variable cambia en el rango de 400 a 600. En la imagen 7 se muestran algunos ejemplos de los valores máximos y mínimos de la escala.
Fig. 7. Mínimo y máximo de la escala
- a) valores de 0 a 500, orden directo
- b) valores de -200 a 400, orden directo
- c) valores de -400 a 0, orden directo
- d) valores de 500 a 0, orden inverso
- e) valores de 200 a 800, orden directo
- f) valores de 0 a -800, orden inverso
La configuración de las marcas de graduación consiste en seleccionar su tamaño y modo de alineación.
La alineación puede ser:
- por el borde interno de la escala
- por el borde externo de la escala
- por el centro
En la imagen 8 se muestran los ejemplos de la alineación de las marcas de graduación de la escala:
- a — alineación por el centro
- b — por el borde interno
- c — por el borde externo
Para la configuración se utiliza la función GaugeSetMarkParameters().
La ubicación de las etiquetas de las marcas de graduación se refiere a la configuración de la escala y se ajusta mediante la función GaugeSetScaleParameters().
En la imagen 8(a) se muestra el ejemplo de ubicación de las etiquetas en la parte interna de la escala, en la imagen 8(b) y 8(c) las etiquetas se ubican en la parte externa.
Para que las etiquetas de las marcas no ocupen mucho sitio en la escala, se recomienda usar el multiplicador. Es un coeficiente en el que van a dividirse todos los valores mostrados de las etiquetas. Los valores del multiplicador pueden ser de 0,0001 a 10000. En la imagen 4(c) se utiliza el multiplicador 100 lo que permite utilizar en las etiquetas de las marcas los números dígitos en vez de los números de tres cifras. En la imagen 1 para el indicador ATR se utiliza el multiplicador 0,0001 lo que permite no utilizar la coma y ceros en las etiquetas. El multiplicador se establece mediante la función GaugeSetScaleParameters().
Fig. 8. Ubicación de las marcas y etiquetas
Las leyendas sirven para mostrar la información adicional y pueden ser de cuatro tipos:
- descripción del instrumento
- unidades de medición
- valor actual
- multiplicador
Usted puede ocultar cualquiera de las etiquetas. Por defecto, se muestra sólo la descripción del instrumento.
La ubicación de la la leyenda se establece mediante el ángulo y el radio. El ángulo se establece en grados y su valor es igual al ángulo entre el rayo que sale verticalmente hacia arriba del centro del instrumento y un segmento imaginario que une el centro del instrumento y el centro de la leyenda. El radio se establece en unidades condicionales. Puede tener los valores de 0 a 10, donde 0 corresponde al radio del centro de la aguja, y el valor 10 corresponde al radio exterior de la escala.
En la imagen 9 se muestra un ejemplo de la posición de las leyendas.
- La leyenda "Profit" (descripción del instrumento) tiene las coordenadas siguientes: ángulo 0 y radio 3.
- La leyenda "0.00" (valor actual) tiene las coordenadas siguientes: ángulo 225 y radio 4.
- La leyenda "USD" (unidades de medición) tiene las coordenadas siguientes: ángulo 215 y radio 8.
Para la configuración de las leyendas se utiliza la función GaugeSetLegendParameters().
Fig. 9. Coordenadas de las leyendas
Importante. Las leyendas no están fijadas en la escala y su ángulo no está conectado con el ángulo de rotación de la escala.
Los rangos destacados de los datos representan un elemento imprescindible de cualquier instrumento indicador. Ayudan a determinar que la magnitud ha obtenido un valor de emergencia o ha entrado en un rango especial. La función GaugeSetRangeParameters() permite establecer hasta cuatro rangos señalados. Para eso hay que establecer los valores extremos y el color de realce. En la imagen 1 el indicador Profit tiene resaltados dos rangos: de 200 a 400 es el color verde que señaliza que ya ha llegado la hora de fijar el beneficio, y de -200 a -400 es el color naranja que avisa sobre una reducción importante.
La función GaugeSetNeedleParameters() se utiliza para establecer el tamaño del centro de la aguja y el tipo de relleno. El tipo de relleno influye en el consumo de recursos puesto que la capa de la aguja se redibuja completamente cada vez que se actualicen los datos. En la imagen 10 se muestran los ejemplos del relleno.
- aguja con relleno usando el algoritmo de suavizado (a)
- aguja con relleno sin usar el algoritmo de suavizado (b)
- contorno de la aguja usando el algoritmo de suavizado sin relleno (c)
Fig. 10. Métodos del relleno de la aguja
Hablaremos sobre las ventajas y desventajas de cada método en las secciones relacionadas con la mejora de la clase CCanvas y evaluación de la intensidad del consumo de los recursos.
3. Funciones
La tabla 1 contiene la lista de funciones para el dibujo de los instrumentos indicadores y configuración de su aspecto visual.
Función | Acción |
---|---|
GaugeCalcLocation | Calcula las coordenadas del centro del instrumento indicador |
GaugeCreate | Crea el instrumento indicador |
GaugeDelete | Elimina el instrumento indicador |
GaugeNewValue | Actualiza la posición de la aguja y el valor mostrado |
GaugeRedraw | Redibuja el instrumento indicador |
GaugeRelocation | Cambia la posición de los objetos del instrumento en el gráfico |
GaugeSetCaseParameters | Establece los parámetros del cuerpo del instrumento indicador |
GaugeSetLegendParameters | Establece los parámetros de la leyenda |
GaugeSetMarkLabelFont | Establece la fuente para las etiquetas de las marcas de graduación de la escala |
GaugeSetMarkParameters | Establece los parámetros de las marcas de graduación de la escala |
GaugeSetNeedleParameters | Establece los parámetros de la aguja |
GaugeSetRangeParameters | Establece los parámetros de los rangos |
GaugeSetScaleParameters | Establece los parámetros de la escala |
Tabla 1. Lista de funciones
Vamos a analizar cada una de las funciones más detalladamente. Están representadas en el mismo orden en el que se recomienda invocarlas durante la inicialización.
Crea el instrumento indicador.
bool GaugeCreate( string name, // nombre del instrumento GAUGE_STR &g, // referencia a la estructura del instrumento int x, // desplazamiento por la horizontal de la esquina de anclaje int y, // desplazamiento por la vertical de la esquina de anclaje int size, // tamaño del instrumento string ObjRel, // nombre del objeto gráfico respecto al cual se establece la posición ENUM_REL_MODE rel_mode, // posicionamiento relativo ENUM_BASE_CORNER corner, // esquina de anclaje bool back, // objetos al fondo uchar scale_transparency, // transparencia de la escala uchar needle_transparency // transparencia de la aguja );
Parámetros
name
[in] Nombre del instrumento indicador. Se utiliza como el prefijo para los nombres de los objetos gráficos que forman parte del instrumento indicador.
g
[out] Referencia a la estructura del instrumento.
x
[in] Distancia en píxeles por el eje X de la esquina de anclaje. En caso del posicionamiento relativo, es la distancia desde el principio local de las coordenadas.
y
[in] Distancia en píxeles por el eje Y de la esquina de anclaje. En caso del posicionamiento relativo, es la distancia desde el principio local de las coordenadas.
size
[in] Tamaño del instrumento. Se representa como el diámetro del cuerpo.
ObjRel
[in] Nombre de otro instrumento respecto al cual se establece la posición. Es pertinente en el caso si se trata del posicionamiento relativo.
rel_mode
[in] Modo del posicionamiento relativo. Puede ser uno de los valores de la enumeración ENUM_REL_MODE.
corner
[in] Esquina del gráfico para el anclaje del objeto gráfico. Puede ser uno de los valores de la enumeración ENUM_BASE_CORNER.
back
[in] Objetos al fondo.
scale_transparency
[in] Grado de transparencia de la escala. Puede adquirir los valores de 0 a 255.
needle_transparency
[in] Grado de transparencia de la aguja. Puede adquirir los valores de 0 a 255.
Valor devuelto
Devolverá true si los objetos de la capa de la escala y de la aguja han sido creados, de lo contrario devolverá false.
Establece los parámetros del cuerpo del instrumento indicador.
void GaugeSetCaseParameters( GAUGE_STR &g, // referencia a la estructura del instrumento ENUM_CASE_STYLE style, // estilo del cuerpo color ccol, // color del cuerpo ENUM_CASE_BORDER_STYLE bstyle, // estilo del borde color bcol, // color del borde ENUM_SIZE border_gap_size // tamaño del espacio entre el borde y elementos de la escala );
Parámetros
g
[out] Referencia a la estructura del instrumento.
style
[in] Estilo del cuerpo. Puede ser uno de los valores de la enumeración ENUM_CASE_STYLE.
ccol
[in] Color del cuerpo.
bstyle
[in] Estilo del borde. Puede ser uno de los valores de la enumeración ENUM_CASE_BORDER_STYLE.
bcol
[in] Color del borde.
gap_size
[in] Tamaño del área entre la línea interior del borde y el elemento más cercano de la escala (en la fig. 3 el tamaño "g"). Puede ser uno de los valores de la enumeración ENUM_SIZE.
Establece los parámetros de la escala.
void GaugeSetScaleParameters( GAUGE_STR &g, // referencia a la estructura del instrumento int range, // rango de la escala int rotation, // ángulo de rotación double min, // valor mínimo (izquierda) double max, // valor máximo (derecha) ENUM_MUL_SCALE mul, // multiplicador de las etiquetas de la escala ENUM_SCALE_STYLE style, // estilo de la escala color col, // color de la escala bool display_arc // mostrar la línea de la escala );
Parámetros
g
[out] Referencia a la estructura del instrumento.
range
[in] Rango de la escala. Se establece como el ángulo formado por dos radio vectores de las marcas extremas de la escala. Puede adquirir los valores de 30 a 320 grados (fig. 3).
rotation
[in] Ángulo de rotación de la escala (fig. 6).
min
[in] Valor mínimo de la escala en caso de la enumeración directa.
max
[in] Valor máximo de la escala en caso de la enumeración directa.
mul
[in] Multiplicador de las etiquetas de la escala. Puede ser uno de los valores de la enumeración ENUM_MUL_SCALE.
style
[in] Estilo de la escala. Puede ser uno de los valores de la enumeración ENUM_SCALE_STYLE.
col
[in] Color de la escala.
display_arc=false
[in] Visualización de la línea de la escala.
Establece los parámetros de las marcas de graduación de la escala.
void GaugeSetMarkParameters( GAUGE_STR &g, // referencia a la estructura del instrumento ENUM_MARK_STYLE style, // estilo de las marcas del instrumento ENUM_SIZE size, // tamaño de las marcas double major_tmi, // paso de las marcas principales int middle_tmarks, // cantidad de las marcas medianas entre las principales int minor_tmarks // cantidad de las marcas pequeñas entre las medianas );
Parámetros
g
[out] Referencia a la estructura del instrumento.
style
[in] Estilo de las marcas de la escala. Puede ser uno de los valores de la enumeración ENUM_MARK_STYLE.
size
[in] Tamaño de las marcas. Puede ser uno de los valores de la enumeración ENUM_SIZE.
major_tmi
[in] Paso de las marcas principales. Las marcas principales se acompañan con las etiquetas de los valores correspondientes.
middle_tmarks
[in] Cantidad de las marcas medianas entre las marcas principales. El valor puede ser cualquier número positivo. No hay límite de la magnitud. Para el valor 0 las marcas medianas no se muestran.
minor_tmarks
[in] Cantidad de las marcas pequeñas entre las marcas medianas (o las principales, si las medianas no se muestran). El valor puede ser cualquier número positivo. No hay límite de la magnitud. Para el valor 0 las marcas pequeñas no se muestran.
Establece la fuente de las etiquetas de las marcas de la escala.
void GaugeSetMarkLabelFont( GAUGE_STR &g, // referencia a la estructura del instrumento ENUM_SIZE font_size, // tamaño de la fuente string font, // la fuente bool italic, // cursiva bool bold, // negrita color col // color );
Parámetros
g
[out] Referencia a la estructura del instrumento.
font_size
[in] Tamaño de la fuente para las etiquetas de las marcas de la escala. Puede ser uno de los valores de la enumeración ENUM_SIZE.
font
[in] Fuente.
italic
[in] Cursiva.
bold
[in] Negrita.
col
[in] Color de la fuente.
Establece los parámetros de la leyenda.
void GaugeSetLegendParameters( GAUGE_STR &g, // referencia a la estructura del instrumento ENUM_GAUGE_LEGEND gl, // tipo de leyenda bool enable, // mostrar leyenda string str, // línea (o parámetro adicional) int radius, // coordenadas - radio double angle, // coordenadas - ángulo uint font_size, // tamaño de la fuente string font, // la fuente bool italic, // cursiva bool bold, // negrita color col // color );
Parámetros
g
[out] Referencia a la estructura del instrumento
gl
[in] Tipo de la leyenda. Puede ser uno de los valores de la enumeración ENUM_GAUGE_LEGEND.
enable
[in] Mostrar la leyenda.
str
[in] Para las leyendas del tipo LEGEND_DESCRIPTION o LEGEND_UNITS es la línea mostrada. Para la leyenda del tipo LEGEND_MUL este parámetro se ignora. Para la leyenda del tipo LEGEND_VALUE es el número de dígitos tras la coma. Puede tener los valores de 0 a 8. Cualquier otro valor se considera como 0. Por ejemplo, la línea “2” significa dos dígitos tras la coma. La línea “hello” significa 0 dígitos tras la coma.
radius
[in] Radio. Distancia en unidades condicionales desde el centro del instrumento hasta el centro de la leyenda (fig. 9).
angle
[in] Coordenada angular. Su valor es igual al ángulo entre el rayo que sale verticalmente hacia arriba del centro del instrumento y un segmento imaginario que une el centro del instrumento y el centro de la leyenda (fig. 9).
font_size
[in] Tamaño de la fuente de la leyenda.
font
[in] Fuente.
italic
[in] Cursiva.
bold
[in] Negrita.
col
[in] Color de la fuente.
Establece los parámetros del rango seleccionado.
void GaugeSetRangeParameters( GAUGE_STR &g, // referencia a la estructura del instrumento int index, // índice del rango bool enable, // mostrar el rango double start, // valor inicial double end, // valor final color col // color );
Parámetros
g
[out] Referencia a la estructura del instrumento.
index
[in] Índice del rango. Puede tener los valores de 0 a 3.
enable
[in] Mostrar la el rango.
start
[in] Valor inicial.
end
[in] Valor final.
col
[in] Color para resaltar el rango.
Establece los parámetros de la aguja.
void GaugeSetNeedleParameters( GAUGE_STR &g, // referencia a la estructura del instrumento ENUM_NCENTER_STYLE ncenter_style, // estilo del centro de la aguja color ncenter_col, // color del centro de la aguja color needle_col, // color de la aguja ENUM_NEEDLE_FILL needle_fill // modo del relleno de la aguja );
Parámetros
g
[out] Referencia a la estructura del instrumento.
ncenter_style
[in] Estilo del centro de la aguja. Puede ser uno de los valores de la enumeración ENUM_NCENTER_STYLE.
ncenter_col
[in] Color del centro de la aguja.
needle_col
[in] Color de la aguja.
needle_fill
[in] Modo del relleno de la aguja. Puede ser uno de los valores de la enumeración ENUM_NEEDLE_FILL.
Redibuja el instrumento indicador. La función debe invocarse después de alterar cualquier parámetro para que esos cambios tengan efecto.
void GaugeRedraw( GAUGE_STR &g // referencia a la estructura del instrumento );
Parámetros
g
[in] Referencia a la estructura del instrumento.
Actualiza la posición de la aguja y el valor mostrado.
void GaugeNewValue( GAUGE_STR &g, // referencia a la estructura del instrumento double v // valor de la variable );
Parámetros
g
[in] Referencia a la estructura del instrumento.
v
[in] Valor actual de la variable.
Elimina los objetos gráficos que forman parte del instrumento indicador. Hay que llamara a la función desde el manejador OnDeinit().
void GaugeDelete( GAUGE_STR &g // referencia a la estructura del instrumento );
Parámetros
g
[in] Referencia a la estructura del instrumento.
Calcula las coordenadas de los objetos del instrumento. Si el posicionamiento relativo está desactivado, siempre devolverá las mismas coordenadas. En caso contrario, las coordenadas pueden diferenciarse de los valores anteriores si el objeto de referencia ha cambiado su posición o tamaño.
bool GaugeCalcLocation( GAUGE_STR& g // referencia a la estructura del instrumento );
Parámetros
g
[in] Referencia a la estructura del instrumento.
Valor devuelto
Devolverá true si los valores obtenidos se diferencian de los anteriores. De lo contrario, devuelve false. Si la función ha devuelto true, hay que llamar a la función GaugeRelocation() para aplicar los parámetros calculados.
Coloca los objetos gráficos que componen el instrumento en el lugar establecido del gráfico. Es necesario llamarla si se indica el posicionamiento relativo y la función GaugeCalcLocation() ha devuelto true.
void GaugeRelocation( GAUGE_STR &g // referencia a la estructura del instrumento );
Parámetros
g
[in] Referencia a la estructura del instrumento.
4. Enumeraciones
La tabla 2 contiene la lista de las enumeraciones que se utilizan como parámetros traspasados a las funciones.
Enumeración | Descripción |
---|---|
ENUM_CASE_BORDER_STYLE | Estilo del borde |
ENUM_CASE_STYLE | Estilo del cuerpo |
ENUM_GAUGE_LEGEND | Tipo de la leyenda |
ENUM_MARK_STYLE | Estilo de las marcas de la escala |
ENUM_MUL_SCALE | Multiplicador de las etiquetas de graduaciones de la escala |
ENUM_NCENTER_STYLE | Estilo del centro de la aguja |
ENUM_NEEDLE_FILL | Modo del relleno de la aguja |
ENUM_REL_MODE | Modo del posicionamiento relativo |
ENUM_SCALE_STYLE | Estilo de la escala |
ENUM_SIZE | Tamaño |
Tabla 2. Lista de enumeración
4.1. ENUM_CASE_BORDER_STYLE
Estilo del borde. Los valores se muestran en la tabla 3.
Identificador | Descripción |
---|---|
CASE_BORDER_NONE | Sin borde |
CASE_BORDER_THIN | Borde fino |
CASE_BORDER_THICK | Borde grueso |
Tabla 3. Valores de ENUM_CASE_BORDER_STYLE
4.2. ENUM_CASE_STYLE
Estilo del cuerpo. Los valores se muestran en la tabla 4.
Identificador | Descripción |
---|---|
CASE_ROUND | Cuerpo circular |
CASE_SECTOR | Cuerpo del tipo sector |
Tabla 4. Valores de ENUM_CASE_STYLE
4.3. ENUM_GAUGE_LEGEND
Tipo de la leyenda. Los valores se muestran en la tabla 5.
Identificador | Descripción |
---|---|
LEGEND_DESCRIPTION | Descripción del instrumento |
LEGEND_UNITS | Unidades de medición |
LEGEND_MUL | Multiplicador de las etiquetas de la escala |
LEGEND_VALUE | Valor actual de la variable |
Tabla 5. Valores de ENUM_GAUGE_LEGEND
4.4. ENUM_MARK_STYLE
Estilo de las marcas de la escala. Los valores se muestran en la tabla 6.
Identificador | Descripción |
---|---|
MARKS_INNER | Alineación de las marcas por el borde interior |
MARKS_MIDDLE | Alineación de las marcas por el centro |
MARKS_OUTER | Alineación de las marcas por el borde exterior |
Tabla 6. Valores de ENUM_MARK_STYLE
4.5. ENUM_MUL_SCALE
Multiplicador de las etiquetas de las marcas de la escala. Los valores se muestran en la tabla 7.
Identificador | Valor | Visualización |
---|---|---|
MUL_10000 | 10000 | х10k |
MUL_1000 | 1000 | х1k |
MUL_100 | 100 | х100 |
MUL_10 | 10 | х10 |
MUL_1 | 1 | No se muestra |
MUL_01 | 0.1 | /10 |
MUL_001 | 0.01 | /100 |
MUL_0001 | 0.001 | /1k |
MUL_00001 | 0.0001 | /10k |
Tabla 7. Valores de ENUM_MUL_SCALE
4.6. ENUM_NCENTER_STYLE
Estilo del centro de la aguja. Los valores se muestran en la tabla 8.
Identificador | Descripción |
---|---|
NDCS_NONE | No se muestra el centro de la aguja |
NDCS_SMALL | Mostrar el pequeño |
NDCS_LARGE | Mostrar el grande |
Tabla 8. Valores de ENUM_NCENTER_STYLE
4.7. ENUM_NEEDLE_FILL
Modo del relleno de la aguja. Los valores se muestran en la tabla 9.
Identificador | Descripción |
---|---|
NEEDLE_FILL | Rellenar la aguja sin suavizado de los bordes |
NEEDLE_FILL_AA | Rellenar la aguja con suavizado de los bordes |
NEEDLE_NOFILL_AA | No rellenar la aguja con suavizado de los bordes |
Tabla 9. Valores de ENUM_NEEDLE_FILL
4.8. ENUM_REL_MODE
Modo del posicionamiento relativo. Los valores se muestran en la tabla 10.
Identificador | Descripción |
---|---|
RELATIVE_MODE_NONE | Posicionamiento relativo desactivado |
RELATIVE_MODE_HOR | Horizontalmente |
RELATIVE_MODE_VERT | Verticalmente |
RELATIVE_MODE_DIAG | Por la diagonal |
Tab. 10. Valores de ENUM_REL_MODE
4.9. ENUM_SCALE_STYLE
Estilo de la escala. Los valores se muestran en la tabla 11.
Identificador | Descripción |
---|---|
SCALE_INNER | Etiquetas de las marcas en el lado interior de la escala |
SCALE_OUTER | Etiquetas de las marcas en el lado exterior de la escala |
Tab. 11. Valores de ENUM_SCALE_STYLE
4.10. ENUM_SIZE
Tamaño. Los valores se muestran en la tabla 12.
Identificador | Descripción |
---|---|
SIZE_SMALL | Pequeño |
SIZE_MIDDLE | Mediano |
SIZE_LARGE | Grande |
Tab. 12. Valores de ENUM_SIZE
5. Macro sustituciones
Coeficientes para los tamaños:
#define DIAM_TO_NDCSL_RATIO 5 //diámetro del centro de la aguja (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_NDCSB_RATIO 7.5 //diámetro del centro de la aguja (large) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_BD_SIZE_S 2 //ancho del borde (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_BD_SIZE_B 5 //ancho del borde (large) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_BD_GAP_S 2.0 //espacio desde el borde del cuerpo hasta los elementos internos del instrumento (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_BD_GAP_M 3.0 //espacio desde el borde del cuerpo hasta los elementos internos del instrumento (middle) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_BD_GAP_L 7.0 //espacio desde el borde del cuerpo hasta los elementos internos del instrumento (large) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_MSZ_MJ_S 3.3 //tamaño de las marcas major de la escala (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MD_S 2.3 //tamaño de las marcas middle de la escala (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MN_S 1.3 //tamaño de las marcas minor de la escala (small) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_MSZ_MJ_M 6.5 //tamaño de las marcas major de la escala (middle) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MD_M 4.8 //tamaño de las marcas middle de la escala (middle) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MN_M 3.0 //tamaño de las marcas minor de la escala (middle) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_MSZ_MJ_L 10.0 //tamaño de las marcas major de la escala (large) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MD_L 7.5 //tamaño de las marcas middle de la escala (large) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MSZ_MN_L 5.0 //tamaño de las marcas minor de la escala (large) en por cientos respecto al diámetro del cuerpo //--- #define DIAM_TO_MFONT_SZ_S 4 //tamaño de la fuente de la etiquetas de las marcas (small) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MFONT_SZ_M 6.5 //tamaño de la fuente de la etiquetas de las marcas (middle) en por cientos respecto al diámetro del cuerpo #define DIAM_TO_MFONT_SZ_L 10 //tamaño de la fuente de la etiquetas de las marcas (large) en por cientos respecto al diámetro del cuerpo
Colores por defecto:
#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. Últimos retoques de la clase CCanvas
6.1. Dibujo del segmento con suavizado
El método LineAA permite trazar un segmento usando el algoritmo de suavizado. Pero al dibujar las marcas de la escala ubicadas circularmente, aparece un problema. Al recalcular las coordenadas del origen y final del segmento del sistema de coordenadas polares al sistema de coordenadas rectangulares, se obtienen los números quebrados que se redondean a los números enteros, y la posición de las marcas se hace “torcida” como se ve en la imagen 11 (b).
Por eso ha sido añadido el método LineAA2 que se diferencia de LineAA sólo en que el tipo de los parámetros de entrada x1, y1, x2, y2 se cambia por double. Eso permite entregar los valores quebrados de las coordenadas y evitar el problema descrito, como se ve en la imagen 11(c).
//+------------------------------------------------------------------+ //| Draw line with antialiasing (with style) v.2 | //+------------------------------------------------------------------+ void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style) { //--- line is out of image boundaries 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; //--- check if(x1==x2 && y1==y2) { PixelSet(int(x1),int(y1),clr); return; } //--- set the line style if(style!=UINT_MAX) LineStyleSet(style); //--- preliminary calculations 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; //--- set 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)); }
Para comparar, en la imagen 11 se muestran los ejemplos del trazado de las marcas usando diferentes métodos:
Fig. 11. Ejemplos del trazado de las marcas usando diferentes métodos (zoom 200%)
6.2. Coloreado del área con bordes suavizados
El método Fill sirve para colorear el área limitada con los segmentos trazados sin usar el algoritmo de suavizado. Si intentamos colorear con él el área limitada con segmentos trazados con el método LineAA, el área se colorea de forma incompleta, como se ve en la imagen 12(a).
Fig. 12. Coloreado del área con bordes suavizados (zoom 200%)
Debido a eso, añadimos el método Fill2. La diferencia consiste en el hecho de que este método no colorea el color del fondo sino cualquier color que se diferencia del color de los segmentos que limitan el área. Eso permite colorear los semitonos que el método Fill no colorea. Puede verlo en la imagen 12(b).
//+------------------------------------------------------------------+ //| Fill closed region with color (v.2) | //+------------------------------------------------------------------+ void CCanvas::Fill2(int x,int y,const uint clr) { //--- check if(x<0 || x>=m_width || y<0 || y>=m_height) return; //--- int index=y*m_width+x; uint old_clr=m_pixels[index]; //--- check if replacement is necessary if(old_clr==clr) return; //--- use pseudo stack to emulate deeply-nested recursive calls int stack[]; uint count=1; int idx; int total=ArraySize(m_pixels); //--- allocate memory for stack 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; //--- left adjacent point idx=index-1; if(x>0 && m_pixels[idx]!=clr) { stack[count]=idx; if(m_pixels[idx]==old_clr) count++; m_pixels[idx]=clr; } //--- top adjacent point 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; } //--- right adjacent point 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; } //--- bottom adjacent point 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; } } //--- deallocate memory ArrayFree(stack); }
A pesar de eso, este método también tiene sus defectos. Si hay un pequeño ángulo agudo, una parte se queda sin colorear, como se muestra en la imagen 12(c). Debido a eso, hemos encontrado la siguiente manera de evitar este problema.
1)Primero, coloreamos el lienzo entero (capa de la aguja) con el color que debe usarse para colorear la aguja:
n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));
2) Luego dibujamos la aguja compuesta de tres segmentos usando el 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) A continuación, coloreamos el área alrededor de la aguja con el color transparente usando el método Fill2:
n.canvas.Fill2(10, 10, 0);
Este método no pretende ser el óptimo pero permite dibujar una aguja bonita.
Fig. 13. Agujas coloreadas con diferentes métodos
En la imagen 13 se muestran las agujas coloreadas con diferentes métodos.
- a) Aguja compuesta de tres segmentos dibujados con el método LineAA2
y coloreada con el método Fill2. - b) Aguja dibujada con el método FillTriangle.
- c) Aguja compuesta de tres segmentos dibujados con el método LineAA2 sin relleno.
Como podemos observar, la aguja mostrada en la imagen 13(b) tiene un aspecto esquinado y pequeñas desviaciones de los ángulos múltiples de 90 grados. Además, podemos ver que la aguja se desplaza del centro. Eso se debe al redondeo de los valores de las coordenadas al recalcular del sistema polar al sistema triángular. No obstante, este método es el más económico desde el punto de vista de la intensidad del consumo de recursos (hablaremos de este asunto más tarde). La aguja de la imagen 13 (c) representa un compromiso entre dos métodos descritos anteriormente. Se compone de los segmentos dibujados con el método LineAA2 pero sin relleno.
7. Ejemplos de aplicación
Vamos a examinar la aplicación de la biblioteca de instrumentos indicadores a través de varios ejemplos.
7.1. Indicador del beneficio actual
Empezamos con lo más simple. El ejemplo de abajo demuestra el mínimo necesario para adicionar un instrumento de aguja en un EA o 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"; // Nombre del indicador input int inp_x = 40; // Desplazamiento por la horizontal input int inp_y = 40; // Desplazamiento por la vertical input int inp_size=300; // Tamaño del indicador input string inp_ObjRel=""; // Nombre del indicador base en caso del posicionamiento relativo input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Modo del posicionamiento relativo input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Esquina de anclaje input bool inp_back=false; // Indicador al fondo input uchar inp_scale_transparency=0; // Grado de transparencia de la escala, 0..255 input uchar inp_needle_transparency=0; // Grado de transparencia de la aguja, 0..255 //--- declaración de la estructura del instrumento GAUGE_STR g0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- creación del instrumento 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); //--- dibujo del instrumento GaugeRedraw(g0); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- eliminación del instrumento GaugeDelete(g0); ChartRedraw(); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- actualización de datos double profit=AccountInfoDouble(ACCOUNT_PROFIT); GaugeNewValue(g0,profit); //--- return(rates_total); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ 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); } } //+------------------------------------------------------------------+
Primero, hay que declarar la estructura del instrumento indicador. Luego continuamos con la función de inicialización donde creamos el instrumento usando GaugeCreate() y llamamos a la función del dibujo GaugeRedraw(). Para la actualización de los datos se utiliza GaugeNewValue(). En este ejemplo se llama desde el manejador OnCalculate().
El instrumento indicador va a ser como se muestra en la imagen 14.
Fig. 14. Presentación del instrumento indicador por defecto
Ahora vamos a añadir las posibilidades de configurar el rango de la escala y ángulo de rotación. Para eso, la lista de los parámetros de entrada se completa con otros dos.
input int inp_scale_range=270; // Rango de la escala, 30..320 grados input int inp_rotation=45; // Giro de la escala, 0..359 grados
Ampliamos el código de inicialización con la llamada a la función para establecer los parámetros de la escala.
//--- establecer los parámetros de la escala y las marcas en la escala GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);
Aquí, además de los parámetros nuevo, también serán establecidos los siguientes:
- nuevos valores del mínimo y del máximo (-200 y 400, respectivamente)
- multiplicador de las etiquetas de las marcas de la escala (MUL_1)
- estilo de la escala (SCALE_INNER — etiquetas de las marcas están dentro)
- color de las marcas de la escala (clrBlack)
Como hemos cambiado los valores extremos de la escala, es deseable corregir el paso de las marcas principales. Para no sobrecargar con el texto, el paso óptimo será 100. Vamos a colocar una marca mediana entre las marcas principales y 4 pequeñas entre las medianas. De esta manera, el paso mínimo de las marcas será 10.
GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);
Resaltamos dos rangos de datos. El rango con el índice 0 que se empieza con 200 y se termina con 400 tendrá el color clrLimeGreen. El rango con el índice 1 que se empieza con -100 y se termina con -200 tendrá el color clrCoral.
//--- resalto de los rangos en la escala GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen); GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);
Vamos a configurar la visualización de las leyendas. Determinamos la descripción del instrumento, unidades de medición y valor actual con un dígito tras la coma. Vamos a ir paso a paso.
Descripción del instrumento:
GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);
Visualización del texto "Profit", radio 3, ángulo 0, tamaño de la fuente 14 unidades condicionales.
Unidades de medición:
GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);
Visualización del texto "USD", radio 8, ángulo 215, tamaño de la fuente 10 unidades condicionales.
Valor actual:
GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);
Aquí la línea “1” significa el formato de la visualización: un dígito tras la coma. Coordenadas: radio 4, ángulo 255. Tamaño de la fuente: 20 unidades condicionales.
Bien, después de los ajustes adicionales, el instrumento tendrá el siguiente aspecto (fig. 15).
Fig. 15. Presentación del instrumento indicador después de los ajustes adicionales
Vamos a ver un ejemplo más complicado: indicador Dashboard. Se muestra en la imagen 1. El instrumento muestra el beneficio, spread, nivel del margen libre en por cientos y los valores actuales de los indicadores técnicos ATR, Force Index y RSI.
Primero, vamos a declarar el array de las estructuras del instrumento.
//--- declaración del array de las estructuras del instrumento GAUGE_STR gg[6];
Luego creamos y configuramos los instrumentos.
El indicador del nivel del margen libre estará en la esquina inferior izquierda. Va a tener las coordenadas absolutas y todos los demás indicadores van a ubicarse respecto a él o respecto al indicador vecino.
//--- creación del instrumento gg00, nivel del margen 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- resalto de los rangos en la escala GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen); GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
Continuamos organizando la línea de abajo. El siguiente es el indicador del beneficio actual.
//--- creación del instrumento gg01, beneficio actual 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- resalto de los rangos en la escala GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen); GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
El siguiente es el indicador del spread.
//--- creación del instrumento 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- resalto de los rangos en la escala GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen); GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
El indicador ATR (arriba a la izquierda) se ubica respecto al indicador del margen libre.
//--- creación del instrumento 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- resalto de los rangos en la escala GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
El indicador RSI se ubica respecto al indicador del spread, encima de él.
//--- creación del instrumento 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
El indicador Force Index se ubica encima del indicador del beneficio actual.
//--- creación del instrumento 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); //--- configuración de los parámetros del cuerpo GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE); //--- configuración de los parámetros de la escala y las marcas en la 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); //--- resalto de los rangos en la escala GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen); GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson); //--- configuración de las marcas del 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); //--- configuración de los parámetros de la aguja GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
Los instrumentos pueden ser diseñados de manera cíclica.
//--- dibujo de instrumentos for(int i=0; i<6;i++) { GaugeRedraw(gg[i]); GaugeNewValue(gg[i],0); }
Cuando ocurre el evento OnCalculate(), recalculamos los valores actuales y llamamos a la función GaugeNewValue() para cada indicador.
//--- actualización de datos //--- spread GaugeNewValue(gg[2],spread[rates_total-1]); //--- beneficio actual double profit=AccountInfoDouble(ACCOUNT_PROFIT); GaugeNewValue(gg[1],profit); //--- nivel del margen double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); GaugeNewValue(gg[0],margin_level); //--- indicador 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]); } //--- 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]); } //--- indicador Force Index 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, nótese que en este ejemplo no tiene sentido llamar GaugeRelocation() desde el evento OnChartEvent(). Aunque aquí se utiliza el posicionamiento relativo, no puede surgir la situación cuando se cambia la ubicación o el tamaño de uno de ellos y hay que recalcular las coordenadas del otro, porque los instrumentos se inicializan al mismo tiempo.
8. Evaluación del consumo de recursos
Cada vez que se actualicen los datos, la capa de la aguja se redibuja completamente. Eso puede pasar con bastante frecuencia, varias veces por segundo, en algunas ocasiones. Por eso el problema de la intensidad del consumo de recursos durante el dibujo de la aguja es bastante grave. Para evaluar el consumo de recursos del procesador relacionados con el diseño de la aguja usando diferentes métodos de relleno, escribiremos un script.
//+------------------------------------------------------------------+ //| test_fill.mq5 | //| Copyright 2015, Decanium | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Decanium" #property version "1.00" #include <Canvas/Canvas2.mqh> CCanvas canvas; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ 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; //--- prueba del relleno con suavizado de los bordes 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"); //--- prueba del contorno suavizado sin relleno 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"); //--- prueba del relleno sin suavizado 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"); } //+------------------------------------------------------------------+
El script inicia cada uno de los modos de dibujo de la aguja 1000 veces en el ciclo y calcula el tiempo gastado para este proceso en milisegundos.
Fig. 16. Resultados de la prueba del consumo de recursos
Como podemos ver de los resultados, el diseño de una aguja con coloreado y suavizado requiere centenas de veces más de tiempo que el diseño con coloreado y sin suavizado, y decenas de veces más de tiempo que el diseño del contorno suavizado sin colorear. En este caso, la belleza realmente tiene su precio.
Conclusión
En este artículo hemos analizado un conjunto de funciones para el diseño de los instrumentos de aguja. El principal objetivo durante la creación de la biblioteca era la sencillez de incorporación de los instrumentos en un Asesor Experto o indicador sin entrar en los detalles del dibujo y geometría. Pues, le toca a Usted decidir si hemos alcanzado este objetivo o no.
La cuestión de la intensidad del consumo de recursos requiere una atención especial. Las computaciones prolongadas en el manejador OnCalculate() pueden provocar la suspensión del terminal. Por eso como compromiso, se recomienda dibujar la aguja suavizada sin colorearla.