plot-widget

The class of the plot-widget.

The plot-widget is a facility for creating two-dimensional plots of
X/Y coordinate pairs, such as scatter plots. (To instead create bar
charts or line graphs for a sequence of items, see
the chart-widget class.)

The plot-widget can be used to present static information or to
dynamically monitor information as it changes. The widget will
automatically lay out its axes and other parts (according to style
properties that you can modify), and can also autocompute a rounded
value range that encloses all of the data values.

Plot subobjects

The following classes are used to create secondary objects that are
used by a plot-widget:

Properties of the plot-widget class

Additional plot-widget properties are provided by
the chart-or-plot
superclass.

plot-widget examples

The following example code demonstrates the two main alternate
techniques for supplying the data for a plot: (1) calling
set-plot-value once for each
datum, and (2) supplying a plot-value-returner function that will be called
as needed to return each value.

For both techniques we will use the following sample data. This is an
array of arbitrary X/Y coordinate pairs. The outermost dimension of
the array represents two independent objects, each of which has its
own set of X/Y pairs. Typically, different icons will be used in a
plot to distinguish the data of different objects.

The second version builds the same plot by supplying a
plot-value-returner function
rather than by calling set-plot-value in a loop. This technique is
typically more efficient because it accesses your data where it
already lives, rather than first accumulating the data gradually into
the plot-widget's own internal representation.

When using a plot-value-returner, you generally need to also
supply a list of chart-objects and a plot-values-max-index value, to
tell the widget the range of indexes over which it should call your
plot-value-returner function.

Here's one more example to illustrate a few more options. This one
adds a second pair of axes with a different scale for a second chart
object. It also specifies some additional properties of the
plot-widget and its secondary objects, to give you a feel for some of
the other modifiable attributes. And it uses a mathematical function
to generate plot values in a circular manner, rather than using real
data as an application typically would.

(let* ((width 500)
(height 600)
(plot-widget
(make-instance 'plot-widget
:title "Doris and Hubert"
:subtitle #.(format nil "This pointless subtitle will wrap ~
automatically to avoid clipping at the sides ~
of the plot-widget.")
:subtitle-color dark-red
:footnote "There's really no need for this footnote."
;; This plot uses a different pair of axes for each object.
;; The :x-axis and :y-axis values for :hubert tell the plot-widget
;; to use the axes on the right and top sides of the plot for that
;; object, rather than the default axes along the left and bottom
;; sides. And the :label properties are printed in the legend
;; to tell the user which objects go with which axes.
:chart-objects (vector (list :id :doris
:label "Doris Lurple (left and bottom axes)")
(list :id :hubert
:label "Hubert Murple (right and top axes)"
:x-axis 2
:y-axis 2))
;; Unlike the previous examples, we specify :draw-lines as true here,
;; to draw lines between the plotted points. True is the default.
;; All of the lines in the plot data will be 2 pixels thick.
:plot-view (make-instance 'plot-view
:icon-images '(:square :circle)
:icon-fill-colors (list blue green)
:line-widths (list 2)
:draw-lines t)
;; Here we create the usual primary axes that lie along the
;; left and bottom sides of the plot.
:x-axis (make-instance 'plot-value-axis
:axis-label "Lower Axis"
:axis-color blue
:range-bottom 0
:range-top 10)
:y-axis (make-instance 'plot-value-axis
:axis-label "Left Axis"
:axis-color blue
:range-bottom 3.0
:range-top 7.0)
;; Since we're using the second pair of axes for this plot,
;; we specify some attributes for those axes here.
:x-axis-2 (make-instance 'plot-value-axis
:axis-label "Upper Axis"
:axis-color (make-rgb :green 192)
:draw-minor-tics t
:draw-minor-labels nil
:minor-tic-length 4
:range-bottom 0
:range-top 10)
:y-axis-2 (make-instance 'plot-value-axis
:axis-label "Right Axis"
:axis-color (make-rgb :green 192)
:draw-minor-tics t
:minor-tic-length 4
:range-bottom 0
:range-top 8
:major-tic-increment 2
:minor-tics-per-major-tic 2)
:chart-legend (make-instance 'chart-legend
:legend-font (make-font-ex :swiss "Arial / ANSI" 12))
;; This is a rather artificial plot-value-returner because
;; it uses a mathematical function to generate points to plot,
;; rather than looking up arbitrary real-world data as usual.
;; But it illustrates returning the various attributes of a
;; plotted point, such as :x-high for an error-range value to
;; the right of a plotted point.
:plot-value-returner
(lambda (plot-widget value-type value-index
object-index object-id)
(declare (ignore plot-widget object-id))
(let* ((factor 2.4)
;; We use some trigonometry here to generate
;; values that go around in a circle. The plot-widget
;; isn't limited to laying out a sequence of items in
;; a single direction as the chart-widget is.
(x (+ 5 (* (+ object-index 2)
(cos (* value-index factor)))))
(y (+ 5 (* (1+ object-index)
(sin (* value-index factor))))))
;; The keywords here are all of the possible values of the
;; value-type argument that is passed to a plot-value-returner
;; function. :x and :y are the coordinates of a point to plot.
;; The low and high values are typically used for an error
;; range, and this plot-widget will draw them as T-bars.
(case value-type
(:x x)
(:y y)
(:x-low nil) ;; Don't draw any T-bars toward the left.
(:x-high (* x 1.07))
(:y-low (* y .95))
(:y-high (* y 1.05))
;; Here we artificially use larger icons for plotted points
;; that are farther to the right (with a larger X coordinate).
(:icon-size (ceiling (+ x 1))))))
;; This tells the plot-widget to call the plot-value-returner
;; function a number of times with the value-index argument
;; varying from 0 through 12.
:plot-values-min-index 0
:plot-values-max-index 12
:right-attachment :right
:bottom-attachment :bottom
:left 0 :top 0 :width width :height height))
(dialog (make-window :example-plot
:class 'dialog
:title "More Complete Example"
:scrollbars nil
:interior (make-box-relative 40 40 width height)
:dialog-items (list plot-widget))))
dialog)