Customizing or Creating a New Chart Type


Introduction

One of the more powerful features of the ChartObject library is the ability to customize an existing chart type or to create new Chart types, different from the ones provided with the library.

Both of these features are made possible because of the object oriented nature of ChartObject, which gives the application the ability to create and insert inside a chart any object from the GraphicObject library.


Inserting Application's Defined Objects

Once a chart object is created, the application has the possibility of inserting its own objects inside the chart or the plot area of the chart. This feature can be used to insert additional labels inside chart, to annotate specific points, to insert user defined symbols into a plot, etc.. Function XintChartInsertObject can be used to insert an object inside a Chart or a Plot2D object. It is not possible to insert objects inside a 3D plot.

Objects inserted inside a chart use the chart coordinate system, which ranges from 0 to 100, with the origin at the upper left corner. Objects inserted in a chart object are clipped to the chart object boundaries.

Objects inserted inside a Plot2D (BarLine, XYPlot, etc.) use the user coordinates specified by the axes attached to the plot. Objects in a plot object are clipped to the plot boundaries. Also, objects inserted in a plot object will be destroyed when the application changes plot type. Use the function XintChartGetComponent to get the object ID of the plot object to use in the XintChartInsertObject function call.

The following code sample illustrates how to insert an object inside a chart.

    XintLine gline;
    Object chart, line;
    ...
    /* create a line object */
    gline.start_x = gline.start_y = 0;
    gline.end_x = gline.end_y = 10;
    line = (Object) XtVaCreateWidget ("Line", 
                                      (WidgetClass)xintLineObjectClass, edit,
                                      XmNline, &gline,
                                      XmNlineThickness, 3, NULL);

    /* insert line object inside a chart object */
    XintChartInsertObject(chart, line);


Customizing an Existing Chart

The following code sample illustrates how to insert an image object inside a chart to create a shaded background. The same technique can be used to insert an image inside a plot. The full listing of the example can be found in directory examples, file chart_3.c.

    Object chart, plot, image;
    Pixel pixel_array[32];
    int   ncolors = 32;
    XintRectangle rect;

    ...

    /*
     * We assume we have allocated 32 colors containing a continuous
     * color spectrum. We now create a pixmap of width 1 and
     * height 32. The ImageObject will automatically interpolate the 
     * pixmap to fit the chart size.
     */
    pixmap = XCreatePixmap(display,
                           RootWindow(display, IntScreenNumber(top_level)),
                           1, ncolors,
                           DefaultDepth(display, IntScreenNumber(top_level)));

    gc = XCreateGC(display, pixmap, 0, NULL);

    for (i=0; i<ncolors; i++) {
         XSetForeground(display, gc, pixel_array[ncolors-i-1]);
         XDrawPoint(display, pixmap, gc, 0, i);
    }

    ...

    /* 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);
    ...

    /*
     * Create image object, "pixmap", as the background of the chart
     */
    rect.x1 = 0;
    rect.y1 = 0;
    rect.x2 = 100;
    rect.y2 = 100;
    image = (Object) XtVaCreateWidget("Image",
                                      (WidgetClass)xintImageObjectClass,
                                      edit,
                                      XmNsensitive, False,
                                      XmNimagePixmap, pixmap,
                                      XmNimageColorRecord, color_record,
                                      XmNrectangle, &rect,
                                      NULL);

    plot = XintChartGetComponent(chart, XintCHART_COMPONENT_PLOT);


    /*
     * Make plot transparent so that we can see the image
     */
    XtVaSetValues((Widget) plot, XmNfillStyle, XintFILL_NONE, NULL);

    /*
     * Insert object into to the chart and lower it
     */
    XintChartInsertObject(chart, image);
    XintEditObjectBack(edit, image);

...


Figure 22: Customized Chart


Creating a New Chart Type

ChartObject has a built-in type for all of the usual kinds of charts. However, it is flexible enough to let the application programmer easily create entirely new chart types. The following example illustrates how to use the Line object to create a chart representing a vector field. The full listing can be found in file chart_field.c in the examples directory.

    #include <Xint/Chart.h>
    #include <Xint/EditObject.h>
    #include <Xint/Line.h>
    #include <math.h>

    extern double drand48();

    #define COUNT 1000

    main(argc, argv)
    int     argc;
    char    *argv[];
    {
        XtAppContext  app_context;
        Widget        top_level;
        Widget        edit;
        Object        chart, plot;
        XintGeometry  chart_geometry;
        XintLimits    limits;
        XintIncrements increment;
        Pixel         pixel;
        register      int i;

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

        /* Create an EditObject widget*/
        edit = XtVaCreateManagedWidget("edit_object",
                                       xintEditObjectWidgetClass,top_level,
                                       XmNwidth, 600, XmNheight, 600,
                                       XmNtitle, "Vector Field", NULL);

        /* Create Chart object */
        chart_geometry.x1 = 0;
        chart_geometry.y1 = 0;
        chart_geometry.x2 = 100;
        chart_geometry.y2 = 100;
        chart = (Object) XtVaCreateWidget("LinePlot",
                                          (WidgetClass)xintChartObjectClass,
                                          edit,
                                          XmNgeometry, &chart_geometry,
                                          XmNchartType, XintCHART_TYPE_LINE,
                                          XmNchartTitle, "Vector FieldPlot",
                                          XmNresourceDialog, False,
                                          NULL);

        plot = XintChartGetComponent(chart, XintCHART_COMPONENT_PLOT);

        /* Set the plot limits and increments */
        limits.minimum = 0;
        limits.maximum = 1.0;
        increment.minor_increment = .25;
        increment.major_increment = .5;
        XtVaSetValues(plot,
                      XmNxLimits, &limits,
                      XmNyLimits, &limits,
                      XmNxIncrements, &increment,
                      XmNyIncrements, &increment,
                      XmNxAxisPlacement, XintPLACEMENT_TOP_BOTTOM,
                      XmNyAxisPlacement, XintPLACEMENT_LEFT_RIGHT,
                      XmNresourceDialog, False,
                      NULL);

        srand48((long) 100);

        pixel = XintLoadColor(XtDisplay(edit), "red");

        for (i=0; i<COUNT; i++) {
             Object arrow;
             XintLine line;
             float x = drand48();
             float y = drand48();
             float size = .05;
             float angle = sin((double) 3.14 * x) + .2 * cos((double) 3.14 * y);

             line.start_x = x;
             line.start_y = y;
             line.end_x = x + size * cos((double) angle);
             line.end_y = y + size * sin((double) angle);

             /*
              * Create arrow object
              * Note: the color and the size of the arrows are the same here,
              *       but they could be set to be different for each object.
              */
             arrow = (Object) XtVaCreateWidget("arrow",
                                               (WidgetClass)xintLineObjectClass,
                                               edit,
                                               XmNlineEnd, XintEND_ARROW,
                                               XmNarrowStyle, XintSTICK,
                                               XmNarrowLength, 4,
                                               XmNtipAngle, 15,
                                               XmNline, &line,
                                               XmNlineThickness, 1,
                                               XmNsensitive, False,
                                               XmNcolor, pixel,
                                               NULL);

             /*
              * Insert arrow object inside the plot
              */
             XintChartInsertObject(plot, arrow);
        }

        XtRealizeWidget(top_level);
        XtAppMainLoop(app_context);
    }


Figure 23: Creating a New Chart Type