
# The Path to Path Enlightenment

```{r echo=FALSE}
knitr::opts_chunk$set(comment = '')
```

## R Graphics: The painters model

R graphics follows a "painters model":  when we draw a shape, it is 
drawn over the top of previous drawing.  For example, the following
code draws a circle followed by a rectangle. Both shapes have an
opaque border, but a semitransparent fill, which allows us to 
see that the rectangle is drawn on top of the circle.

```{r}
library(grid)
```

```{r fig.width=2, fig.height=2}
grid.circle(.6, .6, r=.2, 
            gp=gpar(col=4, lwd=5, fill=adjustcolor(4, alpha=.5)))
grid.rect(.4, .4, width=.4, height=.4, 
          gp=gpar(col=2, lwd=5, fill=adjustcolor(2, alpha=.5)))
```

## Graphics devices: Paths

R graphics output is sent to a *graphics device* either to draw
to the screen, or to record the drawing in a file.  For example,
the `postscript()` device records drawing
in a PostScript file.

The painters model means that a graphics devices is only asked to
draw one shape at a time, e.g., draw a circle *then* draw a rectangle.
For example, the PostScript to draw a circle and a rectangle could look like
the following.  The important part of the code is the clear separation
into two shapes, each starting with a `newpath` and ending with a `fill`
(to fill the shape).

```{r echo=FALSE}
system("convert ps-painters.ps ps-painters.png")
cat(readLines("ps-painters.ps"), sep="\n")
```

![](ps-painters.png)

However, many graphics devices support a more sophisticated drawing
model based on *paths*.  In this model, a single path can be
constructed from more than on shape.  For example, the PostScript
code below draws the same two shapes as before, but draws them
within a single path (and then fills that single path).

```{r echo=FALSE}
system("convert ps-path.ps ps-path.png")
cat(readLines("ps-path.ps"), sep="\n")
```

![](ps-path.png)

The result in this case is a single shape that is the union of the
two individual shapes.  We can get even fancier results by
controlling how "inside" of the path is interpreted.  For example,
the following PostScript code creates exactly the same
single path from the two shapes, but this time runs `eofill` 
(rather than `fill`).  That changes to an "even-odd" fill rule
(from the default "non-zero winding" rule), so the overlap 
between the two shapes within the path is now "outside" the overall
path and we get a shape with a hole in it.

```{r echo=FALSE}
system("convert ps-path-hole.ps ps-path-hole.png")
cat(readLines("ps-path-hole.ps"), sep="\n")
```

![](ps-path-hole.png)

Another element of the more sophisticated path-drawing model is the
ability to construct a path from Bezier curves.  For example,
the following PostScript code describes a path from a series of
straight lines and Bezier curves.

```{r echo=FALSE}
system("convert ps-path-curve.ps ps-path-curve.png")
cat(readLines("ps-path-curve.ps"), sep="\n")
```

![](ps-path-curve.png)

## R-paths

R provides functions to draw more complex shapes, that it calls "paths".
However, we will refer to these as *R-paths* because they are not
as sophisticated as the paths available on some graphics devices.

The following code draws the R version of the combination of circle
and rectangle with a hole where they overlap.

```{r fig.width=2, fig.height=2}
circle <- function(x, y, r) {
    t <- seq(0, 2*pi, length.out=50)
    list(x=x + r*cos(t), y=y + r*sin(t))
}
c <- circle(.6, .6, .2)
grid.path(c(c$x, .2, .2, .6, .6),
          c(c$y, .2, .6, .6, .2),
          id.lengths=c(50, 4), rule="evenodd",
          gp=gpar(col=2, lwd=5, fill=adjustcolor(2, alpha=.5)))
```

Although this shows that *some* aspects of real paths are available
in R graphics, there are some limitations:

* We cannot construct the path from shapes;  we have to generate
  a series of points along the outline of the path ourselves
  (e.g., the `circle()` function above).

* We cannot construct a path from curves.  Even though it is possible to
  draw a stand-alone Bezier curve (e.g., with the 'gridBezier' package),
  we cannot build a path by adding a Bezier curve to other shapes.
  Furthermore, R graphics never asks a graphics device to draw a curve;
  it draws curves by drawing lots of short straight lines that 
  approximate the curve.

## Clipping paths

The addition of clipping paths to the graphics engine actually 
creates situation where we can *accidentally* access some of the
more sophisticated path functionality of some graphics devices.

## Caveats

Note that there *are* a couple of ways that we can generate a more
sophisticated path by combining simple shapes in R graphics

* The `grid::grobCoords()` function.
* The 'gridGeometry' package.


