Artists and Blitting

Background

To generate plots in an efficient manner the Eye employs an animation technique called blitting. Usually when a matplotlib figure is refreshed, like when a new like is plotted for example, the entire figure is redrawn. Axis limits are recomputed, all data points are redrawn, labels generated again, etc. Most of the time, however, few of these objects actually change, meaning a lot of resources were effectively wasted. Blitting addresses that by only drawing what changes. It requires the code to handle the objects created by plotting with matplotlib more directly, since a lot of the automatic features are turned off.

Updating a plot using blitting consists of two steps. Step one is to refresh the background. This includes the splines, axis labels, things that do not change. Nothing is regenerated. A copy of the background is stored in the Blitter class and is merely re-applied. Step two is draw all the animated artists. These include all data lines, cursor indicators, pane borders, etc. Basically anything that ever changes. The artist objects behind each artist, such as the Line2D object behind a plotted line, is not regenerated. If the line has changed, such as the data points changed due to a units change, then the data contained in the Line2D object is altered. A new Line2D object is not created. These “animated” artists are merely redrawn, the bare minimum that needs to be done to add the artist to the screen.

Matplotlib Artists

The bulk of the work in implementing blitting comes from handling the artists. They need to be kept track of, and altered when necessary. Matplotlib does not make this easy as the different plot types seemingly have divergent APIs. The main artists contained in the Eye are:

  1. Line2D: These objects are generated when any data is plotted with the plot method. They have the most basic API calls. Updating the x-data, for instance, is done with:

    line.set_xdata(x_data)
    
  2. PathCollection: A PathCollection is a combination of list of points that describe how to draw a shape (the “Path”) and a list of places to draw that shape (the “Collection”). For the Eye, these come up for any kind of scatte plot, most notably the cursor location (a scatter plot with only one point). When updating the artist to change the data points, the “Path” stays the same and the “Collection” is updated. Since the data underlying a PathCollection is not a series of points that have to be connected in a particular order but instead a set of x,y coordinates, there are no methods like set_xdata. Instead the coordinates, or “offsets” as matplotlib likes to call them, have to be all changed at once with:

    data = numpy.vstack((x_data, y_data))
    line.set_offsets(data)
    
  3. PolyCollection: A PolyCollection describes areas to be drawn, as opposed to the points drawn by Line2D and PathCollections. The area is defined bo a set of vertices that are connected to create a polygon. In the Eye, this artist is used to define the shaded region above and below a line to denote the error range and is generated by fill_between. Unlike a PathCollection or a Line2D object, the PolyCollection does not have a simple method for updating its data. The vertices would be the property to update, and there are no simple API calls to update the vertices. As such, when a PollyCollection needs to be updated, it is simpler to remake the artist.

  4. Patch: A Patch is generally a geometric shape used to highlight or block an area of the figure. Currently the only use for patches in the Eye are the highlights around the current pane. The highlight ia a Rectangle patch whose bounds are set to the very edges of the axis. Since its shape is defined by the axis itself, there is no need to reshape it. The only alteration that must be done to the highlight is to turn it on or off, which is achieved through the set_visible method:

    patch.set_visible(True)
    

    The artist handling and how they are draw in the Eye are handled by three classes discussed below: Drawing , Gallery , and the BlitManager class.

Drawing

The Drawing class, found in sofia_redux.visualization.display.drawing, is the base artist object in the Eye. Each artist object created by matplotlib gets its own Drawing object. The Drawing class also keeps track of various parameters that describe what the artist is, such as the model name, the kind of artist it is, what axes it belongs to, and what pane it belongs to. This makes it easier for the Eye to filter the artists, such as finding all artists that were made with a certain model. Additionally the Drawing class handles all updates to the artist, and provides shortcuts for accessing various attributes of the artist such as color and marker.

Drawing objects are only made by panes. When a new Drawing object is created it parses its artist object to determine what kind of artist it is. Available kinds are:

  • line: Describes an artist created by plotting a dataset

  • error: The error range around a data line

  • cursor: The marker plotted to denote the cursor’s location

  • border: A pane’s border that denotes it is the current pane

  • crosshair: A horizontal and vertical line used when performing a box zoom

  • guide: A vertical line used when selecting a data range to zoom in on or perform a fit on

  • fit_line: The line of a model fit overplotted on the data

  • fit_center: A vertical line at the center of a model fit

  • reference: A vertical line at the wavelength of a reference transistion

  • text: A label attached to the reference line denoting the atomic or molecular species responsible for the transition

  • patch: Any shapes that might end up being drawn

Line

The line drawings holds all artists that describe the data itself. The root matplotlib artists is a Line2D object. To simulate a scatter plot the Line2D object applies markers to the data points and makes the line segments invisible. This keeps the base matplotlib artist class the same regardless of the plot type selected by the user, as the scatter plot method implemented in matplotlib generates a PathCollection object, not a Line2D object.

Error Range

The error range drawings holds PolyCollection artists, generated by fill_between. When the data in a line or cursor subtype changes, the corresponding artist not re-created, merely updated with the correct data points. This is not the case for the error range subtypes. Due to the complexities of how a PolyCollection artist is defined, it is more efficient to recreate the PolyCollection artist with the new data and replace the artist stored in the error range subtype.

Cursor

The cursor drawings only holds PathCollection artists as it shows information about the data at the cursor. All the artists in this subtype only have a single data point attached. The data point has the same x-value as the cursor and the y-value corresponding to the data value at the x-value. The cursor’s y-value is not considered. Each artist in the line subtype has a corresponding artist in the cursor subtype. The cursor artists are generated when the line artist is generated regardless of if the cursor’s location is to be shown. If the cursor’s location is not to be shown, the cursor artist is kept invisible and not updated. It is more efficient to keep an invisible artist in memory than to recreate the artist every time it is needed.

Border

The border drawings hold Rectangle objects. Each Pane has one associated border drawing that describes the border around it. The border is only visible when the associated pane is currently selected. The only updates to border drawings are visibility when the current pane changes and color when the color cycle changes.

Crosshair

The crosshair kind is a unique drawing, as there are only exactly two instances. The crosshair plots a dashed vertical line and a dashed horizontal line across the span of the current axis, crossing at the cursor’s location. The underlying matplotlib artists are Line2D objects.

Guide

The guide drawings are similar to the crosshair drawings except there can be multiple of them. Guides are used to help denote regions of interest for actions such as zooming or fitting curves to the data. The guides take the form of either vertical or horizontal dotted lines, depending of if the task requires selecting x values or y values respectively. The artist is created when the user clicks on a location while a selection mode is active. The location of the mouse is recorded and used to create the guide artist. Once the range selection ends the guides are removed. Guide artists are created and destroyed at a higher rate than any other artist. Their ephemeral status means guide artists do not require updating.

Fit Line

The fit line drawings are created when a model, such as a Gaussian, is fit to a selection of data. The resulting fit is evaluated at all wavelength points then plotted over the data. The fit lines can be turned on and off through the fitting results window. If a fit fails then a fit line drawing is not created.

Fit Center

When a fit line drawing is created an associated fit center drawing is created. The fit center drawing is a vertical line centered at the peak of the model feature, ie the mean of the Gaussian. If the model used to fit the data doesn’t have a feature, such as a linear fit, then the fit center is placed at the midpoint of the wavelength range.

Reference

Reference drawings are created when reference data is loaded into the Eye. Reference data are usually atmospheric absorption lines and are discussed in more detail in NumPy reference . The associated artists are vertical, grey, dashed lines at each reference wavelength. They can be turned on and off in the Reference Data window. These drawings are unique in that they are not considered when autoscaling the data. As such all lines that lie outside the data range are not visible until the x-range is extended. To handle this, when autoscaling the data all reference lines are initially made invisible. Then the axis is autoscaled based only on visible artists, after which the reference lines are made visible again.

Text

Text drawings handle all artists that are blocks of text on a plot. They accompany reference drawings and serve to label what molecular or atomic species is responsible for a given reference transition. Their visibility can be controlled independantly of the reference line, however the labels cannot be made visible with the reference lines invisbile.

Labels are place to the left of their reference line, rotated 90 degrees, at the bottom of the axes. If two reference lines are close together in wavelength space the labels could overlap rendering them illegible. To counter this any time the x-axis limits are changed the text boxes are scraped and regenerated fresh. Upon creation their initial bounding boxes are compared and if two boxes overlap (meaning the labels would overlap) then they are combined. The right most label is deleted and its text appended to the end of the left most label. This process is repeated until no more bounding boxes are overlapping.

Patch

The patch subtype is used for artists describing basic shapes, such as rectangles or circles, and are independent from any data being plotted. The most common patch used is the border around the current pane, or the “pane highlight”. These artists are created when a Pane is assigned an axis and are not changed. The only change to the patches are whether they are visible based on if their pane is the current pane.

Blitting

Blitting is the method used to draw all relevant artists in the Gallery class and is managed by the BlitManager class. Its main task is to draw the plot backgrounds and artists, rendering them to screen. It keeps track of a copy of the background of the canvas.

The background is defined as any artist in the plot that does not represent data. When data are plotted in the Pane class, the artists are all created with their animated flag set to True, which exempts them from being drawn anytime the standard draw function is called. Instead, they will only be drawn when the specifically called with the draw_artist function.

If the artists are updated but the background is not, then the background is restored and the animated artists are drawn upon it. If the background has changed, then the background is cleared and redrawn before the animated artists are drawn. By keeping the background unchanged whenever possible, the processing load of refreshing the plots is greatly reduced, improving performance.