.. _artists-and-blitting: 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: 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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 :class:`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 :ref:`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. .. _gallery: Gallery ------- The primary purpose of the ``Gallery`` class (found at :class:`sofia_redux.visualization.display.gallery`) is to manage all ``Drawing`` objects created by the Eye. There is only one instance of the ``Gallery`` class, kept track of by the ``View`` class. ``Gallery`` only has one attribute, a dictionary that stores the various ``Drawings`` created by panes. The dictionary keys are the different kinds of drawings: + line + line_alt + cursor + cursor_alt + error_range + crosshair + guide + patch + fit + text + ref_line + ref_label Line and cursor drawings are split based on which axis they are on for easy access. Other drawings like error range are not split based on their axes because they only exist on the primary axes. The values of the dictionary are lists. New drawings are appended to the appropriate list. In addition to keeping track of all drawings, the ``Gallery`` also handles drawing updates. Whenever a pane is altered, such as a change in color cycle, axis scale changed, markers changed, the drawings need to be updated to reflect the change. Updates are processed by generating a new ``Drawing`` object with the same identifiers as the target ``Drawing`` but with the ``updates`` attribute populated with a dictionary describing the updates. For example, to update the color to blue, the updates dictionary would look like .. code-block:: python updates = {'color': 'blue'} The updates are then passed to ``Gallery`` which compares the update ``Drawing`` to all ``Drawing`` objects of the same kind looking for a match. When it finds the match the original ``Drawing`` object is given the updates dictionary and the ``Drawing`` class applies the update to the actual matplotlib artist. In order for two ``Drawing`` objects to be considered a match they must have the same: + kind + High model (the model's UUID) + Mid model (order/aperture number) + Data ID (used to identify reference lines and labels) + Pane + Fields (such as "wavepos", "spectral_flux") + Axes (such as "primary", "alt") The ``Gallery`` class also handles things like clearing drawings (such as when a datafile is removed from a plot), determining when reference labels are overlapping and need to be condensed, determining which drawings are involved in a mouse event, and updating artist visibilities. .. _blitting: 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.