ChartObject Introduction


Introduction

ChartObject provides the view component of the MVC architecture described earlier. To create a chart, the application or the end-user needs to create a Chart object. This object automatically creates a number of sub-objects to compose the plot. For example, the chart may create Axis objects, Text objects for annotation, a Legend object, a Plot object, Series objects, etc. The number and type of objects created are dependant upon the chart type as described in more detail below.

Summary of components

The ChartObject library defines the following object classes:

Example

The following example (see example chart_2.c in directory examples) illustrates how to create a 2D bar chart, where the bars are horizontal, stacked and with no space between the bars.

    #include <Xint/EditObject.h>
    #include <Xint/Chart.h>
    #include <Xint/DataGroup.h>
    #include <Xint/DataSampled.h>
    #include <Xint/BarLine.h>

    static String   x_labels[] = {"Houston", "Dallas", "Austin", "San Antonio"};
    static float    d1992[] = { 10.0, 20.0, 20.0, 8};
    static float    d1993[] = { 20.0, 35.0, 32.0, 17};
    static float    d1994[] = { 39.0, 41.0, 37.0, 21};

    main(argc, argv)
    int     argc;
    char    *argv[];
    { 
        XtAppContext  app_context;
        Widget        top_level;
        Widget        edit;
        Object        data_group;
        Object        chart, plot;
        XintGeometry  chart_geometry;

        top_level  = XtAppInitialize(&app_context, "Chart Example",
                                     (XrmOptionDescList)NULL, 0,
                                     &argc, argv, NULL, NULL, 0);

        /* Create an EditObject widget*/
        edit = XtVaCreateManagedWidget("edit_object", 
                                       xintEditObjectWidgetClass,
                                       top_level,
                                       XmNwidth, 400,
                                       XmNheight, 250,
                                       /* make the chart editable */
                                       XmNobjectEditMode, XintEDIT_ADJUST,
                                       NULL);

        /* Create Chart object */
        chart_geometry.x1 = 0;
        chart_geometry.y1 = 0;
        chart_geometry.x2 = 100;
        chart_geometry.y2 = 100;
        chart = (Object) XtVaCreateWidget("BarPlot",
                                          (WidgetClass)xintChartObjectClass, 
                                          edit,
                                          XmNgeometry, &chart_geometry,
                                          XmNchartType, XintCHART_TYPE_BAR,
                                          XmNchartTitle, "Yearly Sales",
                                          XmNshowLegend, True,
                                          NULL);

        /*
         * I want the bars to be horizontal and stacked, with no space
         * between the bars
         */
        plot = XintChartGetComponent(chart, XintCHART_COMPONENT_PLOT);

        XtVaSetValues((Widget) plot,
                      XmNbarOrientation, XintHORIZONTAL,
                      XmNbarStyle, XintSTACKED,
                      XmNclusterWidth, 100,
                      NULL);

        /* Create a data group */
        data_group = XintCreateDataGroup(edit, "Yearly Sales", NULL, 0);

        XtVaCreateWidget("Cities", (WidgetClass)xintDataLabelObjectClass,
                         edit,
                         XmNlabelStrings, x_labels,
                         XmNlabelCount, sizeof(x_labels)/sizeof(String),
                         XmNlabelOrientation, XintLABEL_X,
                         XmNdataGroup, data_group, 
                         NULL);

        XtVaCreateWidget("1992", 
                         (WidgetClass)xintDataSampledObjectClass, edit,
                          XmNdataArray, d1992,
                          XmNcount, sizeof(d1992)/sizeof(float),
                          XmNdataType, XintDATA_TYPE_FLOAT,
                          XmNdataGroup, data_group, 
                          NULL);

        XtVaCreateWidget("1993", 
                         (WidgetClass)xintDataSampledObjectClass, edit,
                         XmNdataArray, d1993,
                         XmNcount, sizeof(d1993)/sizeof(float),
                         XmNdataType, XintDATA_TYPE_FLOAT,
                         XmNdataGroup, data_group, 
                         NULL);

        XtVaCreateWidget("1994", 
                         (WidgetClass)xintDataSampledObjectClass, edit,
                         XmNdataArray, d1994,
                         XmNcount, sizeof(d1994)/sizeof(float),
                         XmNdataType, XintDATA_TYPE_FLOAT,
                         XmNdataGroup, data_group, 
                         NULL);

        /* Associate the data group with the chart object */
        XintChartAssociateData(chart, data_group);

        /* Loop forever */
        XtRealizeWidget(top_level);
        XtAppMainLoop(app_context);
    }

The output from this example is shown below:


Figure 6: Horizontal Stacked Bars Example


Chart Components for 2D Plots

A Chart object consists of a grouping of objects created automatically by the Chart. The figure below illustrates the set of objects created by Chart in the case of a 2D plot.


Figure 7: Components created by a Chart for a 2D plot.

In the figure above, Plot2D is the base class for all 2D plots. Depending on the type of chart selected it could be a BarLine object, a Pie object, a XYPlot object, etc. Series are generic objects that are associated with the data objects. See each appropriate plot section (such as BarLine or Pie) in Chapter 5 for a more complete description of the type of series created.


Figure 8: Components of a 2D chart.

The following table summarizes all the components that may be created inside a Chart object when a 2D plot is displayed.

ComponentObject ClassDescription
Vertical AxisAxisObjectVertical axis associated with the plot area.
Horizontal AxisAxisObjectHorizontal axis associated with the plot area.
Plot2DBarLine
CelllArray
HighLow
Histogram
Pie
XYPlot
Bar chart
Grid of colored cells
High-Low-Open-Close chart
Histogram chart
Pie chart
Line, scattered or area charts.
TitleTextTitle for the chart.
FooterTextFooter for the chart.
LegendLegendLegend for the chart.
SeriesGraphicObject representing a data series. Type can vary (polyline, bars, wedge, etc.) based on the Plot2D class. Unless specified otherwise, use only resources defined in the Graphic class to customize attributes.


Chart Components for 3D Plots

The figure below illustrates the set of objects created by a Chart object in the case of a 3D plot.


Figure 9: Components created by a Chart in the case of a 3D plot.

In the figure above, Plot3D is the base class for all 3D plots. Depending on the type of chart selected it could be a Bar3D or a Surface object. See chart_demo.c in the demos directory, ChartDemo1 subdirectory. Selection 8 is illustrated in Figure 10.


Figure 10: Components of a 3D chart.

The following table summarizes all the components that can be created inside a Chart object when a 3D plot is displayed.

ComponentObject Class Description
Plot3DBar3D
Surface3D
Bar3D chart.
Surface chart.
TitleTextTitle for the chart.
FooterTextFooter for the chart.
LegendLegendLegend for the chart.


Class Hierarchy

All the objects that make up a Chart are based on a class hierarchy, where subclasses inherit the resources and behavior from their superclasses. The following diagram illustrates the class hierarchy for the objects described in this section.


Figure 11: Class Inheritance Diagram.


Customizing Chart Components

All the components created by a chart object can be accessed and modified to fit the application's requirements. Function XintChartGetComponent allows the application to retrieve a component from a chart object. The following code sample illustrates how to use this function to retrieve the ID of the legend object of a chart object and to modify it.

    Object chart, legend;
    ...
    legend = XintChartGetComponent (chart, XintCHART_COMPONENT_LEGEND);
    /* we set the legend border thickness, the # of columns and */
    /* remove the fill.                                         */
    XtVaSetValue((Widget) legend, XmNlineThickness, 3, XmNfillStyle, 
                 XintFILL_NONE, XmNcolumns, 2, NULL);

Series objects, whose number and type depends on the data associated with the chart and the chart type, cannot be retrieved with function XintChartGetComponent. Instead, you should use function XintChartGetSeriesOfData, which requires you to specify a data component. This function returns a list, which contains in most cases one series. It can also contain more than one series, for example, if transposition is set. The following code sample illustrates how to change the line style of the series used to display DataSampled object data_sampled.

    Object chart, data_sampled;
    Object *series_list;
    int count;
    ...
    series_list = XintChartGetSeriesOfData(chart, data_sampled, &count);
    if (series_list != (Object *) NULL) {

        XtVaSetValue((Widget) series_list[0], XmNlineStyle, 
                     XintLINE_ON_OFF_DASH, NULL);

        /* don't forget to free the list */
        XtFree ((char *) series_list);
    }


Transposition

Data objects of type DataSampled are displayed using a data series. For example, a DataSampled object in a BarLine chart is represented as a set of bars, each bar belonging to a different group. The number of bars in a group is equal to the number of DataSampled objects connected to the chart. For a Pie chart, each sample of the DataSampled object is displayed as a wedge, each wedge being located in a different pie.

Chart resource XmNtranspose can be used to transpose the data contained in DataSampled objects. The transpose option is honored by BarLine, Pie and Bar3D plot classes. For example, when transpose is set for a Pie, each sample of a DataSampled object is represented as a wedge inside the same pie. In this case there would be one pie for each DataSampled object connected to the chart.

The Figure below, illustrates transposition for a dataset containing three DataSampled objects as described in the following table (see chart_demo.c in the demos directory, ChartDemo1 subdirectory, selection 17):

DataSampled NameRange (start, inc.)Data Values (in thousands)
Cars1991, 1650, 776, 821, 910
Trucks1991, 1170, 184, 191, 203
Minivans1992, 195, 159, 245 (production started in 1992 !)


Figure 12: Transposition Example.


Combination of Plots

The Chart library lets you combine primitive plot types to build a composite plot. For example, the figure below shows a composite plot made from a HighLow and a BarLine plot.


Figure 13: Combination Plot Example.

The mechanism to combine plots is very flexible. You first create a chart with resource XmNchartType set XintCHART_TYPE_COMBINATION. You then retrieve the ID of the combo plot created and then create as many new plot types as you want inside the combo plot object using function XintComboPlotCreateNewPlot. If several plots have axes displayed on the same side, the axes will automatically be stacked next to each other.

You can combine as many primitive plot types as you want. However, since 3D plot types cannot be made transparent, you should not have more than one 3D plot in a combination plot.


Combination Plot Example

The code example below (see file chart_3.c in directory examples), which produces the output shown above, illustrates how to create a combination plot composed of a Bar plot and a HighLow plot.

    #include <Xint/EditObject.h>
    #include <Xint/Chart.h>
    #include <Xint/ComboPlot.h>
    #include <Xint/DataGroup.h>
    #include <Xint/DataSampled.h>
    #include <Xint/DataLabel.h>
    #include <Xint/AxisObject.h>

    static int high[] =  { 3380, 3410, 3411, 3418, 3400, 3399, 3420, 3425, 3423,
                           3409, 3381, 3372, 3380, 3360, 3359, 3338, 3338, 3312,
                           3310, 3311, 3324, 3348, 3399, 3410, 3412, 3450 };
    static int low[] =   { 3321, 3360, 3350, 3350, 3348, 3349, 3370, 3366, 3369,
                           3350, 3339, 3324, 3322, 3339, 3309, 3268, 3297, 3263,
                           3256, 3264, 3268, 3286, 3325, 3361, 3378, 3390 };
    static int open[] =  { 3350, 3360, 3382, 3355, 3378, 3365, 3375, 3408, 3395,
                           3401, 3375, 3356, 3357, 3349, 3359, 3338, 3303, 3312,
                           3274, 3292, 3289, 3288, 3339, 3389, 3397, 3398 };
    static int close[] = { 3360, 3382, 3355, 3378, 3365, 3375, 3408, 3395, 3401,
                           3375, 3356, 3357, 3349, 3359, 3338, 3303, 3312, 3274,
                           3292, 3289, 3288, 3339, 3389, 3397, 3398, 3450 };
    static int volume[] = { 215123000, 192432123, 194145400, 189900143, 
                            191893456, 204143657, 196143200, 185123456, 
                            186458099, 194312765, 196234234, 184456567, 
                            192334345, 196234246, 214523546, 201234566, 
                            190233456, 199234567, 204455678, 206778456,
                            201233434, 189234356, 192324566, 189234567, 
                            201232356, 196234565 };
                 
    static float label_position[] = { 0.0, 5.0, 10.0, 15., 20.0, 25 };
    static char *date_strings[] = { "1/2/90", "1/9/90", "1/16/90", "1/23/30",
                                    "1/30/90", "2/6/90" };
    
    main(argc, argv)
    int   argc;
    char  *argv[];
    {
      XtAppContext  app_context;
      Widget        top_level, edit;
      Object        data_group, volume_data;
      Object        chart, plot, bar_plot, high_low_plot, axis;
      XintGeometry  chart_geometry;
    
      top_level  = XtAppInitialize(&app_context, "high_low_test",
                                   (XrmOptionDescList)NULL, 0,
                                   &argc, argv, NULL, NULL, 0);
    
      /* Create an edit object */
      edit = XtVaCreateManagedWidget("edit", xintEditObjectWidgetClass,
                                     top_level, 
                                     XmNwidth, 600, 
                                     XmNheight, 300, 
                                     NULL);
    
      /* Create a data group */
      data_group = XintCreateDataGroup(edit, "Dow Jones", NULL, 0);
    
      XtVaCreateWidget("High", (WidgetClass)xintDataSampledObjectClass,
                       edit,
                       XmNdataArray, high,
                       XmNcount, sizeof(high)/sizeof(int),
                       XmNdataType, XintDATA_TYPE_INTEGER,
                       XmNdataGroup, data_group, 
                       NULL);
    
      XtVaCreateWidget("Low", (WidgetClass)xintDataSampledObjectClass,
                       edit,
                       XmNdataArray, low,
                       XmNcount, sizeof(low)/sizeof(int),
                       XmNdataType, XintDATA_TYPE_INTEGER,
                       XmNdataGroup, data_group, 
                       NULL);
    
      XtVaCreateWidget("Open", (WidgetClass)xintDataSampledObjectClass,
                       edit,
                       XmNdataArray, open,
                       XmNcount, sizeof(open)/sizeof(int),
                       XmNdataType, XintDATA_TYPE_INTEGER,
                       XmNdataGroup, data_group, 
                       NULL);
    
      XtVaCreateWidget("Close", (WidgetClass)xintDataSampledObjectClass,
                       edit,
                       XmNdataArray, close,
                       XmNcount, sizeof(close)/sizeof(int),
                       XmNdataType, XintDATA_TYPE_INTEGER,
                       XmNdataGroup, data_group, 
                       NULL);
    
      /* Create a Date label object */
      XtVaCreateWidget("Date", (WidgetClass)xintDataLabelObjectClass,
                       edit,
                       XmNlabelCount, 6,
                       XmNlabelOrientation, XintLABEL_X,
                       XmNlabelPositionArray, label_position,
                       XmNlabelStrings, date_strings,
                       XmNdataGroup, data_group, 
                       NULL);
    
      /* Create volume data */
      volume_data = (Object) XtVaCreateWidget("Volume",
                                       (WidgetClass)xintDataSampledObjectClass,
                                       edit,
                                       XmNdataArray, volume,
                                       XmNcount, sizeof(volume)/sizeof(int),
                                       XmNdataType, XintDATA_TYPE_INTEGER, 
                                       NULL);
    
      /* Create a chart inside the edit object */
      chart_geometry.x1 = chart_geometry.y1 = 0;
      chart_geometry.x2 = chart_geometry.y2 = 100;
      chart = (Object) XtVaCreateWidget("combination_chart",
                                 (WidgetClass)xintChartObjectClass, 
                                 edit,
                                 XmNgeometry, &chart_geometry,
                                 XmNchartType,XintCHART_TYPE_COMBINATION,
                                 XmNchartTitle, "Dow Jones Industrial Average",
                                 NULL);
    
      plot = XintChartGetComponent(chart, XintCHART_COMPONENT_PLOT);
    
      bar_plot = XintComboPlotCreateNewPlot(plot, XintPLOT_TYPE_BAR);
      XintChartAssociateData(bar_plot, volume_data);
    
      high_low_plot = XintComboPlotCreateNewPlot(plot, XintPLOT_TYPE_HIGH_LOW);
      XintChartAssociateData(high_low_plot, data_group);
    
      /* Position the axes */
      XtVaSetValues((Widget) bar_plot,
                    XmNxAxisPlacement, XintPLACEMENT_NONE,
                    XmNyAxisPlacement, XintPLACEMENT_RIGHT, NULL);
    
      XtVaSetValues((Widget) high_low_plot,
                    XmNxAxisPlacement, XintPLACEMENT_BOTTOM,
                    XmNyAxisPlacement, XintPLACEMENT_LEFT, NULL);
    
      /* Change the format of the vertical axis for the volume */
      axis = XintComboPlotGetComponent(plot, XintCHART_COMPONENT_VERTICAL_AXIS,
                                       0);
      XtVaSetValues((Widget) axis, XmNannotationFormat, "%.0f", NULL);
    
      XtRealizeWidget(top_level);
      XtAppMainLoop(app_context);
    }

Plot composition is handled by the ComboPlot class, which is a special class that manages multiple primitive plot types, such as BarLine, XYPlot, HighLow, etc. ComboPlot function XintComboPlotCreateNewPlot allows you to create a new plot of the type you want. The diagram below illustrates the relationships among the various objects created in the example above.


Figure 14:Relationship among the objects created in the combination plot example.


Auto Scaling

When data is associated with a chart, the range of the axes and the annotation increments are calculated automatically based on the data. For example, if your data ranges from 3.8 to 93.7, the Y axis will be adjusted to range from 0 to 100, with an annotation increment set to 10. If you don't want auto-scaling, or if you want to see only a portion of the data (to zoom a plot for example), you can use Plot2D resources XmN[xy]Limits, or Plot3D resources XmN[xyz]Limits to control the range of the axes. Once you set a limit resource for a particular direction, auto-scaling will be turned off for that direction. The limit resources are specified as a pointer to a data structure of type XintLimits which takes the following form:

    typedef struct {
                    float minimum;
                    float maximum;
    } XintLimits;

If you want the auto-scaling to apply only to the minimum or the maximum, you can set one of the fields to constant XintUNDEFINED_FLOAT. For example, to set the minimum value to 0 and to let the maximum be calculated automatically you can specify the following:

    Object plot;
    XintLimits y_limits;
    ...
    y_limits.minimum = 0;
    y_limits.maximum = XintUNDEFINED_FLOAT;

    XtVaSetValues ((Widget) Plot, XmNyLimits, &y_limits, NULL);


Creating And Using Templates

Two functions are available which greatly reduce the effort of the application designer in creating new charts. The first function, XintChartSaveTemplate, saves a single chart or a list of chart objects in a disk file. The chart objects are saved in template form, which means that only the visual attributes of the chart are saved, not the data.

The code sample shown below illustrates how simple it is to save a chart object in template form.

    Object chart;
    ...
    
    XintChartSaveTemplate("template", &chart, 1);

Because only the visual attributes are saved, charts are associated with new data when they are restored in another application. They may then be modified by convenience functions in that application. In fact, any processing that can be performed on chart objects can be performed on the restored templates. The function, XintChartReadTemplate, reads the chart objects stored in a template file created by XintChartSaveTemplate. Using a list of data groups, it associates a data group with each chart object from the file.

The following code shows how to restore a chart from disk.

    Widget edit;
    Object data_group, *list;
    int object_count;
    ...

    list = XintChartReadTemplate(edit, "template", &data_group,
                                 1, &object_count);

Two other functions, XintEditObjectWriteFile and XintEditObjectReadFile, can be used to save and restore objects. These functions read and write the entire object description, including the associated data. They are described in the EditObject Widget Class section of this manual.