Proposed Changes to R Base Graphics

Paul Murrell
Department of Statistics
The University of Auckland
paul@stat.auckland.ac.nz

Motivation

The Changes

How to do it

Unresolved Issues


Motivation

I have two main motivations for making changes to R base graphics:
  1. R base graphics suffers from quite a lot of bugs, inconsistencies, and general unpleasantness. It would benefit from a bit of a redesign under the hood.
  2. The current R base graphics design does not allow the R graphics API to be sufficiently flexible. In particular, there are things I want to be able to do in the grid package that are either impossible or unnecessarily difficult and/or inefficient with the current API.
These motivations lead to a set of changes detailed below. I am aware that other people will have other motivations for making other changes, however, I will be trying to make my changes in a fairly short time frame, which means:
  1. I probably will not be able to make changes for other people on this pass through the code.
  2. I do not want to obstruct other people's potential future changes so please let me know if there appears to be a conflict between what I describe below and what you may be planning. I have attempted to put some comments in bold where I think people might want to have a say.

[top]


The Changes

A Slim Graphics Layer

Currently there is a lot of unnecessary and messy overlap between the code supporting base R graphics, the code for the graphics devices, and the code for what I like to think of as the R graphics engine. If the current situation looks like ...
    [R base graphics]
            [graphics engine]
                    [graphics devices]
I want it to look more like ...
    [R base graphics]  
                     [graphics engine] 
                                      [graphics devices]
which makes it much easier and nicer to do things like ...
    [R base graphics]  
      [grid graphics]
                     [graphics engine] 
                                      [graphics devices]
This obviously implies changes to the graphics API (hopefully to make it cleaner), but also implies a new device API.

An important thing to define is what I think of as the graphics engine. This basically consists of (most of) the calls in graphics.c that start with a G (i.e., GLine, GRect, ...). I want to be able to call these (rather than calling devices directly) because they have (at least) lots of useful stuff to do with clipping that I would not want to write all over again.

An important part of extricating the R base graphics code from the graphics engine and the devices will be a change to passing all par type things explicitly in the calls to the graphics engine (which then passes them explicitly in the calls to the graphics device). At the moment, we have horrible things like the device reaching back up to the R base graphics to find out what the current par settings are. This has the nasty side-effect that, in grid, in order to modify something like the colour that the device uses to draw a rectangle, I have to set par(col) . This is not possible in C code, so I have to do it in R code and suffer a speed penalty. Ideally, devices should know nothing about R base's par.

In a bit more detail, this change would mean not passing an enormous DevDesc structure to the device. In particular, no R-base-graphics-specific information would be passed to the device. The device just gets sent colour, linewidth, font specification, ... There would still be a need for some device state (e.g., what is the "current" device colour) -- threading-related issues here?

Ultimately, all devices would need to be modified. This will hopefully mostly involve just fiddling the arguments to existing functions, plus simple changes along the lines of dd->gp.col becoming col. However, there may be a couple of more involved changes, which may require input from the device driver authors.

Coordinate Systems

With respect to transformations and coordinate systems, this would all have to occur within R base graphics so that the graphics engine only accepts device coordinates.

Minor Extension to the Clipping

I want to generalise the setting of the clipping region (i.e., change GClip and setClipRect). This code is very GRZ-oriented and I want to be able to set an arbitrary rectangular region.

Ideally I would like to be able to set an arbitrary rotated rectangular region, but I will probably leave that for now. Really ideally, I would like to be able to set an arbitrary clipping region, but that's definitely longer term.

Change to GNewPlot

GNewPlot is another hodge-podge of GRZ-oriented bits and graphics engine bits. Needs pulling apart -- some bits go into new graphics engine functions (e.g., some sort of GNewPage function) and the rest becomes an R base graphics newPlot function.

Double Buffering

I want to add double-buffering to the device capabilities. Any help from device maintainers gratefully accepted. This is really a separate issue from the graphics engine clean-up, but would benefit any graphics applications that want to update the graphics device (tcltk demos, grid editing functions, ...).

Fonts

I will probably try to leave fonts (at least the R base graphics font description) in the current pitiful state -- again, trying to fix this as well would probably take too much time.

Add 3D Calculations to Graphics API

Deepayan needs access to the contour and persp calculations to be able to implement the rest of Trellis. This is another non-graphics-engine-clean-up issue.

Embedding R graphics devices

Duncan: are there some basic changes that could be made to the device driver that would assist with the embedding of R?

[top]


How to do it

In order to minimize the impact (i.e., break the least amount of code for the least amount of time), the following strategy is suggested (thanks to Ross Ihaka mostly):
  1. Add new slot in DevDesc structure that indicates whether a device is a "new" device. All existing devices set it to FALSE.
  2. Design new device driver API with parameters like colour and linewidth included in calls to devLine, etc. and make it exported. A first hack at what this might look like is given here.
  3. Write new device driver (e.g., newDevX11.c) based on current device code, but conforming to new device driver API.
  4. Write new graphics engine functions within grid based on current graphics engine code, but which make use of new device driver API. This converts grid to new graphics engine and new device drivers (but means that grid will not work with existing device drivers).
  5. Modify G* calls in graphics.c to use new device driver calls if device is a "new" device. (Do NOT replace these calls with new graphics engine functions in grid yet -- that may have to wait until post-1.4 because it will require rewriting all of R base graphics to use the new graphics engine.) Test this on newX11 device.
    NOTE that a potentially serious downside to this step is that, until R base graphics is switched to using the new graphics engine, we would have a lot of duplicated code sitting in graphics.c and grid (because the new graphics engine functions in grid will be mostly just copies of the G* functions in graphics.c). This could cause problems if, for example, a bug is fixed in graphics.c and the corresponding fix is not made in grid -- when the new graphics engine replaces the code in graphics.c the bug is lost.
  6. Convert existing devices, one by one, switching on the "new" flag as they become ready.
This would get us to the stage of having: a new device driver interface (with all existing devices conforming to it), a new graphics engine implemented in grid, and R base graphics working just as it does at the moment. It would allow me to speed up and tidy up grid and fill in some of the important missing bits. It would also mean that R base graphics would be ready for changes to use the new graphics engine and for a potential clean up. Once that has been done, the graphics API could be changed (basically to expose the new graphics engine) so that other "external" graphics code can make use of it (the new device driver API would not be advertised until this time).

[top]


Unresolved Issues

[top]