All Manuals > CAPI User Guide and Reference Manual > 14 Graphic Tools drawing objects


14.2 Higher level - drawing graphs and bar charts

The higher level Graphic Tools functions all generate a "drawing-object-spec" (a drawing-object or a list) which can then be displayed by inclusion in the hierarchy under an objects-displayer (potentially via a pinboard-objects-displayer).

The functions are geared towards producing graphs of (mathematical) functions and bar charts. The function generate-grid-lines is used to generate grid of lines. The function generate-labels is used to generate labels, with the intention that these labels will match the grid lines.

The functions generate-graph-from-pairs and generate-graph-from-graph-spec are used to generate the actual graph. The graph is actually a sequence of straight lines connecting consecutive points (neighbouring points in the x dimension), but by giving it enough points the graph can be made to look smooth. Currently there is no smoothing option.

generate-graph-from-pairs receives the points as a list of lists (x y). generate-graph-from-graph-spec takes a basic-graph-spec which you make by calling make-basic-graph-spec. The graph spec contains a function which computes the y value corresponding to the supplied x value, and information (start, step and range) which specifies the x values to use. The basic-graph-spec is intended to simplify writing code that repeatedly draws graphs with similar attributes.

generate-bar-chart generates the bars of a bar chart, with an optional title for each bar.

To show something useful, you will normally combine the results of generate-grid-lines, generate-labels and one of generate-graph-from-pairs, generate-graph-from-graph-spec or generate-bar-chart (typically by just using cl:list), and then position and scale the result using the geometry functions (position-object, fit-object, position-and-fit-object), and the result of this will be put into a hierarchy under an objects-displayer or pinboard-objects-displayer.

Note that when you scale (using fit-object or position-and-fit-object), you effectively change the units of drawing inside the scaled object. You can therefore generate the graph in its natural coordinates, and then put in the correct dimensions on the screen. The example below generates a graph with size of 18x9, and then uses fit-object with the same width and height, which scales the graph to fit the full area that it is supplied. We also give it some margin using position-object.

We then use the result (fitted-graph-with-margin) both as the drawing-object of a pinboard-objects-displayer and the drawing-object of an objects-displayer which also contains the pinboard-objects-displayer. In the pinboard-objects-displayer we also add a red rectangle to show the area of the pinboard-objects-displayer. The result is that the the same graph is displayed twice: once inside pinboard-objects-displayer and once inside the whole objects-displayer. If you resize the window, you see that the outer graph resizes, while the inner graph stays the same (because the pinboard-objects-displayer does not change size).

(let* ((graph
        (lw-gt:generate-grid-lines :horizontal-count 18
                                   :vertical-count 9
                                   :right-thickness 3
                                   :major-x-step 4
                                   :major-y-step 3
                                   :thickness 1
                                   :major-thickness 2
                                   :major-color :blue
                                   :color :green))
       (fitted-graph (lw-gt:fit-object graph 18 9))
        (lw-gt:position-object fitted-graph
                               :left-margin 10
                               :right-margin 10
                               :top-margin 10
                               :bottom-margin 10))
         (lw-gt:make-draw-rectangle 0 0 1 1
                                    :foreground :red
                                    :thickness 2
                                    :scale-thickness nil)
                          1 1))
       (pinboard-object (lw-gt:make-pinboard-objects-displayer
                         (list red-rectangle fitted-graph-with-margin)
                         :x 45 :y 45 :width 400 :height 400)))
  (setq *pane* (capi:contain (make-instance 'lw-gt::objects-displayer
                               :description (list pinboard-object)
                               :drawing-object fitted-graph-with-margin
                :best-width 500 :best-height 500)))

For the pinboard-object to resize, you need to resize it explicitly.

The following function moves the first pinboard object:

(defun move-first-pinboard-object (pane x y width height)
   #'(lambda (pane x y width height)
       (let ((po (car (capi:layout-description pane))))
         (setf (capi:static-layout-child-geometry po)
               (values x y width height))))
                              pane x y width height))

Now this moves the pinboard object, and resizes the grid inside it (as well as the red rectangle):

(move-first-pinboard-object *pane* 20 60 420 300)

More extended are examples are in

(example-edit-file "graphic-tools/bar-chart-example") (example-edit-file "graphic-tools/graph-example")


CAPI User Guide and Reference Manual (Unix version) - 25 Feb 2015