Download MetaTrader 5

Drawing Dial Gauges Using the CCanvas Class

21 July 2015, 12:34
Serhii Shevchuk
5
4 313

Table Of Contents

Introduction

It all started when I first acquainted myself with the CCanvas class. When it came to practice, I stumbled upon an idea to draw a gauge indicator. My first gauges were pretty crude, but eventually they have been supplemented by new elements and become visually pleasing. And as a result, I have a small library now which allows to add a dial gauge to an indicator or an EA in a simple and easy manner. In this article, we will give consideration to structure of gauges, get acquainted with functions necessary for drawing and setting visual appearance, and assess resource intensity.

Dial gauges

Fig.1. Dial gauges


1. Coordinates and Anchor

There are two types of positioning a gauge on a chart: absolute and relative.

In case of absolute positioning, coordinates represent distances in pixels from an anchor corner along X and Y axis.

In case of relative positioning, local origin of coordinates is created according to the specified type of the relative positioning. When the vertical type is selected, the origin is located below or above a reference object (if an upper or a lower anchor corner is selected respectively). When the vertical type is selected, it is located on the left or on the right in the direction from the anchor corner. In this case, specified coordinates represent an offset from their local origin. Positive offsets lead to moving an object away from the reference object. In case of negative offsets, the object will encroach on the reference one.

The reference object can be represented only by an object of another gauge. It is essential that both objects will have the same anchor corner.

Fig. 2 depicts an example of relative positioning.

Relative positioning

Fig.2. Relative positioning of gauges

Let's review settings of each gauge:

  • The "gg01" gauge: relative positioning is disabled. Horizontal offset — 40, vertical offset — 40.
  • The "gg02" gauge: relative positioning — horizontal, reference object — "gg01". Horizontal offset from the local origin of coordinates (point A) — 15, vertical offset — 0.
  • The "gg03" gauge: relative positioning — vertical, reference object — "gg01". Horizontal offset from the local origin of coordinates (point B) — 0, vertical offset — 15.
  • The "gg04" gauge: relative positioning — vertical, reference object — "gg02". Horizontal offset from the local origin of coordinates (point C) — 50, vertical offset — 15.

Relative positioning facilitates input setting if the chart has several indicators containing gauges. If you decide to change size of one gauge, other gauges' coordinates will be automatically recalculated.

The GaugeCreate() function sets the positioning type and coordinates.


2. Gauge Elements

The dial gauge consists of two graphical objects. One of them is called a scale layer, another one is called a needle layer. Both graphical objects have the same coordinates. The needle layer is placed over the scale layer. The gauge name set in input parameters serves as a prefix for names of both objects. For example, if the gauge name is "Gauge01", the scale layer will be called "Gauge01_s", and the needle layer will have name "Gauge01_n".

Fig.3 depicts structure of the gauge.

Fig.3. Gauge structure

Fig.3. Gauge structure

The scale layer contains:

  • border (1)
  • scale graduation marks (5, 6, 7)
  • scale graduation labels (4)
  • highlighted ranges (2, 12)
  • legends (3, 10, 11)

Legends are distinguished by purposes:

  • gauge description (3)
  • units of measure (11)
  • current value (10)
  • multiplier of scale labels (omitted)

Scale graduation is divided into:

  • major (7)
  • middle (5)
  • minor (6)

Only major graduation points have labels. Graduation step is set as a numeric value. Middle graduation step is calculated depending on a specified number of middle marks between major ones. Minor graduation step is calculated depending on a specified number of minor marks between middle ones. Minor and middle graduation can be omitted.

The needle layer contains:

  • needle (8)
  • needle center (9)


2.1. Sizes

Fig.3 depicts sizes of some gauge elements:

  • d — gauge size which corresponds to the diameter of the external contour line of the gauge
  • b — border size
  • g — size of space between a border and scale elements
  • c — size of the needle center.

NB. The gauge diameter is the only size set in pixels ("d" in fig.3). All other elements and fonts are set in conditional units and their sizes are calculated as percentage of the diameter. It is made to facilitate scaling. Change the diameter, and all other sizes will be proportionally recalculated. Calculation coefficients are listed in the Macro Substitution section and can be modified by the user.


2.2. Body Shape

There are two types of gauge body shape: a circle and a sector. The sector shape is more convenient if the scale range angle is less than 180 degrees.

Gauge shape

Fig.4. Gauge shape

Fig.4 depicts one circle gauge (a) and two sector shape gauges (b, c). The GaugeSetCaseParameters() function is used to set the desired body shape.


2.3. Scale

This is the most important element of the gauge. Data readability depends on its appearance. The scale should not be overcomplicated, but at the same time it must be informative enough. Selection of scale extreme values, as well as a step of major marks, call for special attention. The GaugeSetScaleParameters() function allows to set the scale range, its rotation and extreme values (minimum and maximum). Minimum value can be on the left (direct order) or on the right (inverse order).

The scale range is an angle contained by two radius vectors of scale extreme values. It is demonstrated in Fig.5.

Scale range

Fig.5. Scale range

The scale rotation is an angle of deviation of the scale range angle bisector from the line which upwardly and vertically comes from the gauge center. It is demonstrated in Fig.6.

Scale rotation angle

Fig.6. Scale rotation angle

Combining the scale range angle and the rotation angle can help you to set the gauge appearance in a quite flexible manner. Fig.4(c) demonstrates a gauge with 90 degree range and 45 degree rotation.

Maximum and minimum scale values are important parameters which should be selected depending on the range of allowed values of the displayed variable. Zero mark can be omitted for the sake of convenience. There is no point in drawing the scale from zero if your variable changes in the range from 400 to 600. Fig.7 depicts some examples of maximum and minimum scale values.

Maximum and minimum scale values

Fig.7. Maximum and minimum scale values

  • a) values from 0 to 500, direct order
  • b) values from -200 to 400, direct order
  • c) values from -400 to 0, direct order
  • d) values from 500 to 0, inverse order
  • e) values from 200 to 800, direct order
  • f) values from 0 to -800, inverse order


2.4. Graduation Marks

Graduation mark setting lies in selecting size of marks and aligning method.

Alignment can be as follows:

  • inner edge of the scale
  • outer edge of the scale
  • center

Fig.8 depicts examples of aligning scale graduation marks:

  • a — center
  • b — inner edge
  • c — outer edge

The GaugeSetMarkParameters() function is used for setting.

Position of labels for marks is referred to scale settings and adjusted using the GaugeSetScaleParameters() function.

Fig.8(a) depicts an example of positioning labels inside the scale, Fig.8(b) and 8(c) — outside the scale.

It is recommended to use a multiplier, a coefficient all displayed values will be divided by, so the labels won't occupy too much space on the scale. The multiplier can have values from 0.0001 to 10000. Fig.4(c) depicts an example of applying a multiplier equal to 100, which allowed to use one-digit numbers instead of three-digit numbers in labels. Fig.1 depicts a situation where we use a multiplier equal to 0.0001 for ATR, which allowed not to use the decimal point and zeros in labels. The GaugeSetScaleParameters() function sets the multiplier.

Positioning marks and labels

Fig.8. Positioning marks and labels


2.5. Legends

Legends are meant for displaying supplemental information and can be of four types:

  • gauge description
  • units of measure
  • current value
  • multiplier

Any legend can be hidden. Only the gauge description is displayed by default.

Legend positioning is set by the angle and the radius. The angle is set in degrees and its value is equal to the angle between the line, which upwardly and vertically comes from the gauge center, and an imaginary segment, which connects the gauge center and the legend center. The radius is set in conditional units. It can have values from 0 to 10, where 0 corresponds to the radius of the needle center and 10 corresponds to the outer radius of the scale.

Fig.9 depicts an example of legend positioning.

  • The "Profit" legend (gauge description) has following coordinates: angle - 0, radius - 3.
  • The "0.00" legend (current value) has following coordinates: angle - 225, radius - 4.
  • The "USD" legend (units of measure) has following coordinates: angle - 215, radius - 8.

The GaugeSetLegendParameters() function is used for setting the legend parameters.

Legend coordinates

Fig.9. Legend coordinates

NB. Legends are not fixed on the scale and their angles are not connected with the scale rotation angle.


2.6. Highlighted Ranges

Highlighted data ranges represent an inherent element of any gauge. They help to see that the variable has taken on an emergency value or entered some special range. The GaugeSetRangeParameters() function can set up to four highlighted ranges. To do so, you need to set extreme values and color for highlighting. Fig.1 depicts the Profit indicator with two highlighted ranges: from 200 to 400 is the green range, which indicates time for fixing profit, and from -200 to -400 is the orange range, warning about large drawdown.


2.7. Needle

The GaugeSetNeedleParameters() function sets the size of the needle center and the type of area fill. The type of area fill influences on resource intensity of the indicator, as the needle layer is fully redrawn every time after data update. Fig.10 depicts example of area fill.

  • filled needle with the use of antialiasing algorithm (a)
  • filled needle without the use of antialiasing algorithm (b)
  • not filled needle with the antialiased contour line (c)

Methods of needle area fill

Fig.10. Methods of needle area fill

Pros and cons of each method are described in sections devoted to modification of the CCanvas class and resource intensity assessment.


3. Functions

Table 1 lists functions for drawing gauges and setting their appearance.

Function
Behavior
GaugeCalcLocation
Calculates gauge center coordinates
GaugeCreate
Creates the gauge
GaugeDelete
Deletes the gauge
GaugeNewValue
Updates position of the needle and displayed value
GaugeRedraw
Redraws the gauge
GaugeRelocation
Changes location of gauge objects on the chart
GaugeSetCaseParameters
Sets gauge body parameters
GaugeSetLegendParameters
Sets legend parameters
GaugeSetMarkLabelFont
Sets font of graduation labels
GaugeSetMarkParameters
Sets scale graduation parameters
GaugeSetNeedleParameters
Sets needle parameters
GaugeSetRangeParameters
Sets range parameters
GaugeSetScaleParameters
Sets scale parameters

Table 1. List of functions

Let's consider each function in depth. They are represented in the order in which we recommend to call them when initializing.


3.1. GaugeCreate

Creates the gauge.

bool GaugeCreate(
   string name,              // gauge name
   GAUGE_STR &g,             // reference to the gauge structure
   int x,                    // horizontal indent from the anchor corner
   int y,                    // vertical indent from the anchor corner
   int size,                 // gauge size
   string ObjRel,            // name of a graphical object relatively to which the position is set
   ENUM_REL_MODE rel_mode,   // relative positioning
   ENUM_BASE_CORNER corner,  // anchor corner
   bool back,                // objects on the background
   uchar scale_transparency, // scale transparency
   uchar needle_transparency // needle transparency 
 );

Parameters

 name

   [in]  Gauge name. Used as a prefix for names of graphical objects which compose the gauge.

 g

   [out]  Reference to the gauge structure.

 x

   [in]  Distance in pixels from the anchor corner along the X axis. In case of relative positioning — distance from the local origin of coordinates.

 y

   [in]  Distance in pixels from the anchor corner along the Y axis. In case of relative positioning — distance from the local origin of coordinates.

 size

   [in]  Size of the gauge. Represented as the body diameter.

 ObjRel

   [in]  Name of another graphical object relatively to which the position is set. Remains pertinent only if relative positioning is set.

 rel_mode

   [in]  Method of relative positioning. Can have any value listed in ENUM_REL_MODE.

 corner

   [in]  Chart corner to anchor the graphical object. Can have any value listed in ENUM_BASE_CORNER.

 back

   [in]  Objects on the background.

 scale_transparency

   [in]  Scale transparency level. Can have values from 0 to 255.

 needle_transparency

   [in]  Needle transparency level. Can have values from 0 to 255.

Return value

  Returns true if objects of the scale layer and the needle layer have been created. Otherwise returns false.


3.2. GaugeSetCaseParameters

Sets gauge body parameters.

void GaugeSetCaseParameters(
   GAUGE_STR &g,                  // reference to the gauge structure
   ENUM_CASE_STYLE style,         // body style
   color ccol,                    // body color
   ENUM_CASE_BORDER_STYLE bstyle, // border style
   color bcol,                    // border color
   ENUM_SIZE border_gap_size      // size of space between a border and scale elements
);

Parameters

 g

   [out]  Reference to the gauge structure.

 style

   [in]  Body style. Can have any value listed in ENUM_CASE_STYLE.

 ccol

   [in]  Body color.

 bstyle

   [in]  Border style. Can have any value listed in ENUM_CASE_BORDER_STYLE.

 bcol

   [in]  Border color.

 gap_size

   [in]  Area between the internal line of the border and the nearest scale element ("g" in fig.3). Can have any value listed in ENUM_SIZE.


3.3. GaugeSetScaleParameters

Sets scale parameters.

void GaugeSetScaleParameters(
   GAUGE_STR &g,           // reference to the gauge structure
   int range,              // scale range
   int rotation,           // angle of rotation
   double min,             // minimum value (left)
   double max,             // maximum value (right)
   ENUM_MUL_SCALE mul,     // multiplier of scale labels
   ENUM_SCALE_STYLE style, // scale style
   color col,              // scale color
   bool display_arc        // display scale line
);

Parameters

 g

   [out]  Reference to the gauge structure.

 range

   [in]  Scale range. Set as an angle contained by two radius vectors of scale extreme marks. Can have values from 30 to 320 degrees (Fig.5).

 rotation

   [in]  Scale rotation angle (Fig.6).

 min

   [in]  Minimum scale value in case of direct number assignment.

 max

   [in]  Maximum scale value in case of direct number assignment.

 mul

   [in]  Multiplier of scale labels. Can have any value listed in ENUM_MUL_SCALE.

 style

   [in]  Scale style. Can have any value listed in ENUM_SCALE_STYLE.

 col

   [in]  Scale color.

 display_arc=false

   [in]  Display scale line.


3.4. GaugeSetMarkParameters

Sets scale graduation parameters.

void GaugeSetMarkParameters(  
   GAUGE_STR &g,          // reference to the gauge structure
   ENUM_MARK_STYLE style, // style of scale marks
   ENUM_SIZE size,        // size of marks
   double major_tmi,      // major mark interval
   int middle_tmarks,     // number of middle marks between major ones
   int minor_tmarks       // number of minor marks between middle ones
);

Parameters

 g

   [out]  Reference to the gauge structure.

 style

   [in]  Style of scale graduation. Can have any value listed in ENUM_MARK_STYLE.

 size

   [in]  Mark size. Can have any value listed in ENUM_SIZE.

 major_tmi

   [in]  Step of major graduation marks. Major marks are accompanied by labels with relevant values.

 middle_tmarks

   [in]  Number of middle marks between major ones. Can have any positive value. No size constraints. If set to 0, middle marks are not displayed.

 minor_tmarks

   [in]  Number of minor marks between middle ones (or between major marks if middle ones are not displayed). Can have any positive value. No size constraints. If set to 0, minor marks are not displayed.


3.5. GaugeSetMarkLabelFont

Sets font of graduation marks.

void GaugeSetMarkLabelFont(
   GAUGE_STR &g,        // reference to the gauge structure
   ENUM_SIZE font_size, // font size 
   string font,         // font
   bool italic,         // italic
   bool bold,           // bold
   color col            // color
);

Parameters

 g

   [out]  Reference to the gauge structure.

 font_size

   [in]  Font size of graduation labels. Can have any value listed in ENUM_SIZE.

 font

   [in]  Font.

 italic

   [in]  Italic.

 bold

   [in]  Bold.

 col

   [in]  Font color.


3.6. GaugeSetLegendParameters

Sets legend parameters.

void GaugeSetLegendParameters(
   GAUGE_STR &g,         // reference to the gauge structure
   ENUM_GAUGE_LEGEND gl, // legend type
   bool enable,          // display legend
   string str,           // string (or a complementary parameter)
   int radius,           // coordinates - radius
   double angle,         // coordinates - angle
   uint font_size,       // font size
   string font,          // font
   bool italic,          // italic
   bool bold,            // bold
   color col             // color
);

Parameters

 g

   [out]  Reference to the gauge structure

 gl

   [in]  Legend type. Can have any value listed in ENUM_GAUGE_LEGEND.

 enable

   [in]  Display the legend.

 str

   [in]  This is a displayed string for legends of LEGEND_DESCRIPTION or LEGEND_UNITS type. This parameter is ignored for a legend of the LEGEND_MUL type. Number of decimal places for a legend of the LEGEND_VALUE type. Can have values from "0" to "8". Any other values are perceived as "0". For example, the string "2" means two decimal places. The string "hello" means 0 decimal places.

 radius

   [in]  Radius. Distance from the gauge center to the legend center in conditional units (fig. 9).

 angle

   [in]  Angular coordinates. Its value is equal to the angle between the line, which upwardly and vertically comes from the gauge center, and an imaginary segment, which connects the gauge center and the legend center (Fig.9).

 font_size

   [in]  Legend font size.

 font

   [in]  Font.

 italic

   [in]  Italic.

 bold

   [in]  Bold.

 col

   [in]  Font color.


3.7. GaugeSetRangeParameters

Sets highlighted range parameters.

void GaugeSetRangeParameters(
   GAUGE_STR &g, // reference to the gauge structure
   int index,    // range index
   bool enable,  // display range
   double start, // initial value
   double end,   // final value
   color col     // color
);

Parameters

 g

   [out]  Reference to the gauge structure.

 index

   [in]  Range index. Can have values from 0 to 3.

 enable

   [in]  Display range.

 start

   [in]  Initial value.

 end

   [in]  Final value.

 col

   [in]  Color to highlight the range.


3.8. GaugeSetNeedleParameters

Sets needle parameters.

void GaugeSetNeedleParameters(
   GAUGE_STR &g,                     // reference to the gauge structure
   ENUM_NCENTER_STYLE ncenter_style, // needle center style
   color ncenter_col,                // needle center color
   color needle_col,                 // needle color
   ENUM_NEEDLE_FILL needle_fill      // method of needle area fill
);

Parameters

 g

   [out]  Reference to the gauge structure.

 ncenter_style

   [in]  Style of the needle center. Can have any value listed in ENUM_NCENTER_STYLE.

 ncenter_col

   [in]  Needle center color.

 needle_col

   [in]  Needle color.

 needle_fill

   [in]  Method of needle area fill. Can have any value listed in ENUM_NEEDLE_FILL.


3.9. GaugeRedraw

Redraws the gauge. The function has to be called after changing any parameters to apply these changes.

void GaugeRedraw(
   GAUGE_STR &g       // reference to the gauge structure
); 

Parameters

 g

   [in]  Reference to the gauge structure.


3.10. GaugeNewValue

Updates position of the needle and displayed value.

void GaugeNewValue(
   GAUGE_STR &g,     // reference to the gauge structure
   double v          // variable value
);

Parameters

 g

   [in]  Reference to the gauge structure.

 v

   [in]  Current value of variable.


3.11. GaugeDelete

Deletes graphical objects which compose the gauge. Call the function from the OnDeinit() handler.

void GaugeDelete(
   GAUGE_STR &g      // reference to the gauge structure
);

Parameters

 g

   [in]  Reference to the gauge structure.


3.12. GaugeCalcLocation

Calculates coordinates of gauge objects. If relative positioning is disabled, it will always return same coordinates. Otherwise coordinates may differ from previous values if the reference object has changed its location or size.

bool GaugeCalcLocation( 
   GAUGE_STR& g         // reference to the gauge structure
);

Parameters

 g

   [in]  Reference to the gauge structure.

Return value

  Returns true if coordinate values differ from previous ones. Otherwise returns false. If the function returns true, call the GaugeRelocation() function to apply calculated parameters.


3.13. GaugeRelocation

Locates graphical objects which compose the gauge on the specified spot of the chart. Necessary to call if relative positioning is set and the GaugeCalcLocation() function has returned true.

void GaugeRelocation(
   GAUGE_STR &g       // reference to the gauge structure
);

Parameters

 g

   [in]  Reference to the gauge structure.


4. Enumerations

Table 2 lists enumerations used as function parameters.

Enumeration
Description
ENUM_CASE_BORDER_STYLEBorder style
ENUM_CASE_STYLE
Body style
ENUM_GAUGE_LEGEND
Legend type
ENUM_MARK_STYLE
Style of scale graduation
ENUM_MUL_SCALE
Multiplier of scale graduation labels
ENUM_NCENTER_STYLEStyle of the needle center
ENUM_NEEDLE_FILLMethod of needle area fill
ENUM_REL_MODEMethod of relative positioning
ENUM_SCALE_STYLEScale style
ENUM_SIZESize

Table 2. List of enumerations


4.1. ENUM_CASE_BORDER_STYLE

Border style. Values are listed in table 3.

Identifier
Description
CASE_BORDER_NONE
No border
CASE_BORDER_THINThin border
CASE_BORDER_THICK
Thick border

Table 3. Values of ENUM_CASE_BORDER_STYLE

4.2. ENUM_CASE_STYLE

Body style. Values are listed in table 4.

Identifier
Description
CASE_ROUND
Circular body
CASE_SECTOR
Sector-type body

Table 4. Values of ENUM_CASE_STYLE

4.3. ENUM_GAUGE_LEGEND

Legend type. Values are listed in table 5.

Identifier
 Description
LEGEND_DESCRIPTIONGauge description
LEGEND_UNITSUnits of measure
LEGEND_MULMultiplier of scale labels
LEGEND_VALUECurrent value of variable

Table 5. Values of ENUM_GAUGE_LEGEND

4.4. ENUM_MARK_STYLE

Style of scale graduation. Values are listed in table 6.

Identifier
 Description
MARKS_INNERAligning marks by the inner edge
MARKS_MIDDLEAligning marks by the center
MARKS_OUTERAligning marks by the outer edge

Table 6. Values of ENUM_MARK_STYLE

4.5. ENUM_MUL_SCALE

Multiplier of scale graduation labels. Values are listed in table 7.

 IdentifierMeaning
Display
MUL_1000010000
 х10k
MUL_10001000
 х1k
MUL_100100
 х100
MUL_1010
 х10
MUL_11
 Not displayed
MUL_010.1
 /10
MUL_0010.01
 /100
MUL_00010.001
 /1k
MUL_000010.0001
 /10k

Table 7. Values of ENUM_MUL_SCALE

4.6. ENUM_NCENTER_STYLE

Style of the needle center. Values are listed in table 8.

Identifier
Description
NDCS_NONEDon't display the needle center
NDCS_SMALLDisplay small
NDCS_LARGEDisplay large

Table 8. Values of ENUM_NCENTER_STYLE

4.7. ENUM_NEEDLE_FILL

Method of needle area fill. Values are listed in table 9.

 Identifier Description
NEEDLE_FILLFill the needle without antialiasing of edges
NEEDLE_FILL_AAFill the needle with antialiasing of edges
NEEDLE_NOFILL_AADon't fill the needle but apply antialiasing of edges

Table 9. Values of ENUM_NEEDLE_FILL

4.8. ENUM_REL_MODE

Method of relative positioning. Values are listed in table 10.

 Identifier Description
RELATIVE_MODE_NONERelative positioning is disabled
RELATIVE_MODE_HORHorizontally
RELATIVE_MODE_VERTVertically
RELATIVE_MODE_DIAGDiagonally

Table 10. Values of ENUM_REL_MODE

4.9. ENUM_SCALE_STYLE

Scale style. Values are listed in table 11.

Identifier
 Description
SCALE_INNERGraduation labels inside the scale
SCALE_OUTERGraduation labels outside the scale

Table 11. Values of ENUM_SCALE_STYLE

4.10. ENUM_SIZE

Size. Values are listed in table 12.

Identifier
 Description
SIZE_SMALLSmall
SIZE_MIDDLEMiddle
SIZE_LARGELarge

Table 12. Values of ENUM_SIZE


5. Macro Substitution

Coefficients for sizes:

#define DIAM_TO_NDCSL_RATIO   5   //needle center diameter (small) as percentage of the body diameter
#define DIAM_TO_NDCSB_RATIO   7.5 //needle center diameter (large) as percentage of the body diameter
//---
#define DIAM_TO_BD_SIZE_S     2 //border width (small) as percentage of the body diameter
#define DIAM_TO_BD_SIZE_B     5 //border width (large) as percentage of the body diameter
//---
#define DIAM_TO_BD_GAP_S      2.0 //space from the body border to inner elements of the gauge (small) as percentage of the body diameter
#define DIAM_TO_BD_GAP_M      3.0 //space from the body border to inner elements of the gauge (middle) as percentage of the body diameter
#define DIAM_TO_BD_GAP_L      7.0 //space from the body border to inner elements of the gauge (large) as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_S      3.3 //size of major scale (small) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_S      2.3 //size of middle scale (small) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_S      1.3 //size of minor scale (small) graduation as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_M      6.5 //size of major scale (middle) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_M      4.8 //size of middle scale (middle) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_M      3.0 //size of minor scale (middle) graduation as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_L      10.0 //size of major scale (large) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_L      7.5  //size of middle scale (large) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_L      5.0  //size of minor scale (large) graduation as percentage of the body diameter
//---
#define DIAM_TO_MFONT_SZ_S    4   //font size of scale (small) graduation labels as percentage of the body diameter
#define DIAM_TO_MFONT_SZ_M    6.5 //font size of scale (middle) graduation labels as percentage of the body diameter
#define DIAM_TO_MFONT_SZ_L    10  //font size of scale (large) graduation labels as percentage of the body diameter

Default colors:

#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. Modifying the CCanvas Class


6.1. Drawing a Segment Using Antialiasing Algorithm

The LineAA method allows to draw a segment using the antialiasing algorithm. But one problem turns up when we draw circular scale marks. When we convert coordinates of the segment's initial and final points from polar to rectangular coordinate system, we have fractional numbers which round up to a whole number. Consequently marks look crooked, which is shown in fig.11(b).

That's why we have added the LineAA2 method, which differs from LineAA by the fact that the type of x1, y1, x2, y2 input parameters has been changed to double. Thus we can deliver fractional values of coordinates and get rid of the mentioned problem, which is vividly seen in figure 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));
  } 

Fig.11 depicts examples of various methods of drawing scale marks:

Method of drawing scale marks

Fig. 11. Various methods of drawing scale marks (increased by 200%)


6.2. Filling an Area with Antialiased Edges

The Fill method is meant for filling an area bounded by segments drawn without use of antialiasing algorithm. If we use this method for filling an area bounded by segments drawn by the LineAA method, the area will be filled incompletely, which is seen in fig.12(a).

Filling an Area with Antialiased Edges

Fig.12. Filling an area with antialiased edges (increased by 200%)

So we have added the Fill2 method. The difference is that it fills not the background color, but any color different from color of segments which bound the area. It allows to fill undertints, which cannot be done using the Fill method. Fig.12(b) depicts an example.

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

Nevertheless, this method also has disadvantages. If there is a small acute angle, its part will remain unfilled, which is shown in fig.12(c). So we have found a way out of this problem.

   1) First the whole canvas (needle layer) is filled with color meant for the needle:

n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));

   2) Then we draw the needle composed of three segments using the LineAA2 method:

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) After this we fill the area around the needle with transparent color using the Fill2 method:

n.canvas.Fill2(10, 10, 0);

This method is not the best one, but it allows to draw the proper needle.

Methods of needle area fill

Fig.13. Needles filled using different methods

Fig.13 depicts needles filled using different methods.

  • a) The needle composed of three segments drawn using the LineAA2 method and filled using the Fill2 method.
  • b) The needle drawn via the FillTriangle method.
  • c) The unfilled needle composed of three segments drawn using the LineAA2 method.

As we can see, the needle shown in fig.13(b) is craggy and has small deviation from angles divisible by 90 degrees. Besides, we can see that the needle is shifted from the center which is caused by rounding off values of coordinates when we convert them from polar to rectangular coordinate system. But at the same time this method is the most practical in the context of resource intensity (we will come back to this issue later). The needle shown in fig.13(c) is a trade-off in two methods described above. It is composed of three segments drawn using the LineAA2 method but without area fill.


7. Application Examples

Let's review the application of the gauge library through several examples.


7.1. Indicator of Current Profit

We will start with the simplest one. The following example demonstrates the basic set for adding the gauge to an EA or an indicator.

//+------------------------------------------------------------------+
//|                                       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";                  // Indicator name
input int inp_x = 40;                                // Horizontal offset
input int inp_y = 40;                                // Vertical offset
input int inp_size=300;                              // Indicator size
input string inp_ObjRel="";                          // Name of the base indicator in case of relative positioning
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Relative positioning mode
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Anchor corner
input bool inp_back=false;                           // Indicator is in the background
input uchar inp_scale_transparency=0;                // Scale transparency level, 0..255
input uchar inp_needle_transparency=0;               // Needle transparency level, 0..255

//--- declaration of the gauge structure
GAUGE_STR g0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- creating the gauge
   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);
//--- drawing the gauge
   GaugeRedraw(g0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- deleting the gauge
   GaugeDelete(g0);
   ChartRedraw();
  }    
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- updating readings
   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);
     }
  }
//+------------------------------------------------------------------+

First we need to declare the gauge structure. Then we continue with the initialization function where we create the gauge using the GaugeCreate() function and call the drawing function — GaugeRedraw(). GaugeNewValue() can be used to update readings. In this example, it is called from the OnCalculate() handler.

The gauge will look as shown in fig.14.

Indicator appearance, default parameters

Fig.14. Default appearance of the gauge

Now we will add ability to set the scale range and the rotation angle. It will add two parameters to the list of inputs.

input int inp_scale_range=270;   // Scale range, 30..320 degrees
input int inp_rotation=45;       // Scale rotation, 0..359 degrees

Now we extent the initialization code with the call of the function for setting scale parameters.

//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);

Complementary to new input parameters we will also set:

  • new maximum and minimum values (-200 and 400 respectively)
  • multiplier of scale graduation labels (MUL_1)
  • scale style (SCALE_INNER — graduation labels are inside)
  • color of labels (clrBlack)

As we have changed scale extreme values, it is desirable to correct a step of major marks. The best value is 100, as it excludes text abundance. At that we will place one middle mark between two major ones and 4 minor marks between two middle ones. Thus we will have a minimum step between marks equal to 10.

   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);

Now we will highlight two data ranges. The range having index 0, which starts with 200 and ends with 400, will be highlighted with the clrLimeGreen color. The range having index 1, which starts with -100 and ends with -200, will be highlighted with the clrCoral color.

//--- highlighting ranges on the scale
   GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);

Now we are going to set legends. We determine the gauge description, units of measure and the current value with one decimal place. Let's review them one by one.

Gauge description:

   GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);

Displayed string is "Profit", radius is 3, angle is 0, font is 14 conditional units.

Units of measure:

   GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);

Displayed string is "USD", radius is 8, angle is 215, font is 10 conditional units.

Current value:

   GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);

Here the string "1" means the format of displaying (one decimal place). Coordinates: radius is 4, angle is 255. Font size is 20 conditional units.

Thus, after we have performed some additional settings, the gauge will look as shown in fig.15.

Indicator of Current Profit

Fig.15. Appearance of the gauge after additional setting


7.2. The Dashboard Indicator

Now we are going to review more complicated example, namely the Dashboard indicator. It is shown in fig.1. The indicator displays the current profit, spread, free margin as percentage and current values of ATR, Force Index and RSI.

First we will declare the gauge structure array. 

//--- declaration of the gauge structure array
GAUGE_STR gg[6];

Then we will create and adjust gauges.

The margin level indicator will be placed in the bottom left corner. It will have absolute coordinates, and all other indicators will be located regarding to this indicator or the neighboring one.

//--- building the gg00 gauge, margin level
   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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen);
   GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

We continue arranging the bottom row. The next is the current profit indicator.

//--- building the gg01 gauge, current profit
   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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

The spread indicator closes the bottom row.

//--- building the gg02 gauge, 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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen);
   GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

The ATR indicator (the left one in the upper row) is placed relatively to the free margin indicator.

//--- building the gg03 gauge, 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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

The RSI indicator is placed relatively to the spread indicator (above).

//--- building the gg04 gauge, 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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

The Force Index indicator is placed above the current profit indicator.

//--- building the gg05 gauge, 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);
//--- setting body parameters
   GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   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);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen);
   GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson);
//--- setting text labels
   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);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Gauges can be drawn in a cyclic manner.

//--- drawing gauges
   for(int i=0; i<6;i++)
     {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],0);
     }

When the OnCalculate() event occurs, we recalculate current values and call the GaugeNewValue() function for each indicator.

//--- updating readings
//--- spread
   GaugeNewValue(gg[2],spread[rates_total-1]);
//--- current profit   
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(gg[1],profit);
//--- margin level
   double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   GaugeNewValue(gg[0],margin_level);
//--- the ATR indicator
   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]);
     }
//--- the RSI indicator
   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]);
     }
//--- the Force Index indicator
   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]);
     }

Please note, that there is no point to call GaugeRelocation() from the OnChartEvent() event in the given example. Although relative positioning is used here, we do not need to recalculate coordinates of gauges if position or size of one of them has changed, as gauges are initialized all at once.


8. Resource Intensity Assessment

The needle layer is fully redrawn whenever readings update. It may happen quite often, even several times per second in some instances. That is why the problem of resource intensity of drawing the needle is quite acute. We will write a small script to assess the CPU overhead for drawing the needle using various area fill methods.

//+------------------------------------------------------------------+
//|                                                    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;
//--- testing the area fill with antialiased edges
   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");
//--- testing the antialiased contour without filling
   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");
//--- testing the area fill without antialiasing
   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");
  }
//+------------------------------------------------------------------+

The script launches each method of drawing the needle 1000 times in a cycle and measures the time spent for this process in milliseconds.

Resource Intensity Test

Fig.16. Results of the resource intensity test

As you can see by the results, the filled needle with antialiased edges is drawn hundreds of times longer than the filled needle without antialiasing and tens of times longer than just an antialiased contour line without filling. In this case, beauty really has its price.


Conclusion

In this article, we have reviewed the set of functions for drawing gauges. The main target of the library creation was the simplicity of adding gauges to an EA or an indicator without delving into the details of drawing and geometry. Though, it's up to you to decide whether we have reached this target.

Special attention should be paid to the resource intensity. Time-consuming computations in the OnCalculate() handler can cause the terminal suspension. So we recommend applying the compromise method of drawing the needle (antialiasing without filling).

Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1699

Attached files |
dashboard.mq5 (22.69 KB)
canvas2.mqh (171.52 KB)
gauge_graph.mqh (154.69 KB)
Last comments | Go to discussion (5)
JD4
JD4 | 21 Jul 2015 at 20:46
Awesome idea, as many people drive and are used to seeing round gauges and meters.  Gives a new way to see standard indicators in a new format if they are willing to put the work into making them look like this.
PCWalker
PCWalker | 26 Jul 2015 at 12:02
MetaQuotes Software Corp.:

New article Drawing Dial Gauges Using the CCanvas Class has been published:

Author: Serhii Shevchuk

JD4:
Awesome idea, as many people drive and are used to seeing round gauges and meters.  Gives a new way to see standard indicators in a new format if they are willing to put the work into making them look like this.
Well, It's very nice JD4, that you see this idea as great. Thank you for your contribution,Serhii, and for your time into my childhood fascinations.
Erdem Sen
Erdem Sen | 3 Aug 2015 at 18:01

great atricle,

i've been thinkin about some indicators in this format but didnt have the time to learn how to.., you made it very easy to learn..

Pandu Swasonoputra
Pandu Swasonoputra | 3 Sep 2015 at 04:58

Great article...

Mohammad Soubra
Mohammad Soubra | 14 Sep 2015 at 20:37
Fantastic

I have got more information + new ideas has been came

Thanks
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.