Objective Chart : PART II Programmer’s Guide : Chapter 16 Chart Extensions : Deriving From SRGraphDisplay
Deriving From SRGraphDisplay
Often, you do not want to define a whole new graph or axis type— you just need to modify the behavior of a standard type in some way. In this case, you need to create a new class derived from SRGraphDisplay (or one of the graph-type specific classes) and override the one or two functions that perform the behavior that needs to be changed. Most of the drawing functions in Objective Chart are virtual functions so that they can be easily overridden.
As described in “SRGDisplayBase”, SRGraphDisplay::DrawForeground() breaks the drawing process into four basic steps:
1. Prepare()— determines axis limits (autoscaled or fixed).
2. DrawLabels()— generates and displays axis labels (interaction between the axis labels and the data display rectangle generally requires a two-pass process).
3. DrawAxis()— draws a frame around the data display rectangle with tick marks and grid lines.
4. DrawData()— displays the data items.
NOTE >> Steps 3 and 4 may be interchanged depending on the setting of GetGridOrder() in the display’s style member.
SRGDisplayBase implements these general functions which call specialized helper functions (defined in SRGraphDisplay) appropriate for the selected graph and axis types. These specialized helper functions are the ones most commonly overridden. Look in the general functions in SRGDisplayBase to determine which function handles your specific graph or axis type.
When functions are mentioned in this chapter, XXX is often used as a placeholder. Table 22 shows real examples of these functions:
Table 22 – Examples of Graphing Functions 
Generic Function
Examples
PrepareXXX()
PrepareYScale()
DrawXXXLabels()
DrawClassicLabels()
DrawScatterLabels()
DrawPieLabels()
DrawXXXAxis()
DrawClassicAxis()
DrawScatterAxis()
DrawXXXData()
DrawLineData()
DrawScatterData
DrawPieData()
The PrepareXXX() functions only need to be modified if a different data format is needed. For example, if you are adding error bars to a Line graph using the CScale member of the data objects to store the upper and lower values, you should override PrepareYScale() to consider these values when scanning for the axis limits.
DrawXXXLabels() is often overridden to change the format or spacing of axis labels.
DrawXXXAxis() draws tick marks and grid lines at the positions determined by DrawXXXLabels(), so it is not modified often.
DrawXXXData() draws the bars, lines, wigets, and pie wedges according to the axis scaling determined by PrepareXXX(). Override it to change how the data items are drawn— to draw the error bars in the example above.
Modifications to PrepareXXX(), DrawXXXAxis(), or DrawXXXData() depend on the special requirements of the application, so little guidance can be given for these extensions. However, a few tips can be given for changing the appearance or spacing of axis labels by overriding DrawXXXLabels().
Overriding DrawXXXLabels()
 
Changing the Standard Behavior of Axis Labels
The scale objects (SRGDecimalScale, SRGLogScale, SRGIndexScale, etc.) that create the axis labels have settable parameters that control the appearance and spacing of axis labels. Because SRGraphDisplay::DrawXXXLabels() creates, configures, updates, and destroys the scale objects internally, these parameters are not readily accessible to the programmer.
Changing the standard behavior requires the definition of a new class derived from SRGraphDisplay (or one of the graph-type specific display classes) with an override of the appropriate DrawXXXLabels() function. A copy of the body of the function from the library provides the starting point for your modifications. Once your have taken this step, the axis labels can be changed in a variety of ways.
A more technically correct procedure is to define a custom axis type and override DrawCustomLabels() as described later in this chapter.
NOTE >> Be sure to modify your chart setup code to instantiate your new class instead of SRGraphDisplay and set the axis style to your custom axis type, if appropriate.
For the following discussion, we assume that DrawClassicLabels() is the function being overridden. The specific changes described may not be appropriate for other DrawXXXLabels() functions.
Modifying the Interval Between Labels
You can control the number of labels that are generated by setting the resolution (the interval between labels). The scaling system in Objective Chart makes setting the resolution for SRGDecimalScale unnecessary in most cases. But it is still useful for SRGIndexScale. In this case, the resolution is a skip factor: 1 gives a label for each index, 2 gives a label for every other index, 3 every third, etc.
To modify the interval between labels, call SetResolution() during the setup of the scale object before calling CreateLabels().
 
SRGDecimalScale *pDS=new SRGDecimalScale;
pDS->SetResolution(10.0); // ten centimeters
or
SRGIndexScale *pDS=new SRGIndexScale;
pDS->SetResolution(5); // every fifth data object
SetResolution(0) lets the scale object chose the interval between labels.
Changing the Number of Digits and Adding Text for Units
To change the number of digits or add text for units, call SetFormatString() during the setup of the SRGDecimalScale object before CreateLabels().
 
SRGDecimalScale *pDS=new SRGDecimalScale;
pDS->SetFormatString(“%g cm”);
or
pDS->SetFormatString(“%.3f”);
Avoiding Overlapping Labels
To avoid overlapping or too-close-together labels, call SetCheckOverlaps(TRUE) for the label block in your override of DrawXXXLabels().
You can choose to hide the overlapping labels or to move them. For example,
 
SRGDecimalScale* pDS=new SRGDecimalScale;
pDS->SetCheckOverlaps(TRUE);
pDS->GetStyle()->SetLabelStyle(CX_LABELBLOCK_MOVELABELS);
// or SetLabelStyle(CX_LABELBLOCK_HIDELABELS);
Alternatively, you can rotate the labels to 45 or 90 degrees to avoid overlap.
 
SRGDecimalScale* pDS=new SRGDecimalScale;
pDS->GetStyle()->SetAutoOrientation(FALSE);
pDS->SetOrientation(45.);
pDS->SetLocationPoint(SRGraphLabel::MidRight);
Creating Unevenly Spaced or other Specific Labels
If you really want to control which labels are created, you can derive a new class from one of the scale classes and override CreateLabels() to generate any labels you want.
For example, you could override SRGIndexScale::CreateLabels() to generate a label for each index i such that (i+5)%nResolution==0. Then modify your DrawXXXLabels() override to use your new scale class.
Or, you could override SRGDecimalScale::CreateLabels() to generate a fixed number, say 5, of labels.
Derive your own class from SRGDecimalScale and override CreateLabels() to generate labels however you want.
Override the appropriate DrawXXXLabels() routine for the graph type— generally DrawClassicLabels()— to utilize your new scale class instead of SRGDecimalScale.
The SimpleCustom sample project overrides DrawClassicLabels() to orient the x-axis labels at 45 degrees and to add units to the y-axis labels.
The USDChart sample is a more elaborate customization. Its display class overrides DrawLineData(), DrawClassicLabels(), DrawClassicAxis(), DrawScatterData(), and DrawScatterLabels() to create a Y-axis with the origin at the top and with values increasing downward. SRGraphPosition is subclassed also, and the view class contains an override of GetPosition() that uses the new graph position class. This axis could be used for displaying temperature versus depth in the ocean, for example.