piecepackr has limited support to project 3D board games (like the hundreds of piecepack games) onto a 2D diagram. It is hoped that in the future piecepackr will offer better 3D projection support with the help of OpenGL.

## Orthographic projections

By default piecepackr's `grid.piece` function makes an approximate "top view" orthographic projection with pieces drawn later "placed on top" of (and potentially hiding) pieces drawn earlier:

```
library("piecepackr")
g.p <- function(...) { grid.piece(..., default.units="in") }
g.p("tile_back", x=0.5+c(3,1,3,1), y=0.5+c(3,3,1,1))
g.p("tile_back", x=0.5+3, y=0.5+1)
g.p("tile_back", x=0.5+3, y=0.5+1)
g.p("die_face", suit=3, rank=5, x=1, y=1)
g.p("pawn_face", x=1, y=4, angle=90)
g.p("coin_back", x=3, y=4, angle=180)
g.p("coin_back", suit=4, x=3, y=4, angle=180)
g.p("coin_back", suit=2, x=3, y=1, angle=90)
```

piecepackr makes an exact "top view" orthographic projection under the following conditions:

- All pieces are placed "flat" parallel to the game table. Although most piecepack games do this there are exceptions like San Andreas where some tiles may shift from flat to "leaning" (and vice versa) during the course of a game.
- The game doesn't use pyramids (although the "top" view of pyramids is an okay if not exact orthographic projection).
- Any pawns used are (generalized) "meeple" pawns laid face down or face up.

## Oblique projections

With a little more effort by the piecepackr programmer one can also make oblique projections with `grid.piece` which makes it much easier to tell when pieces have been placed on top of other pieces:

```
g.p <- function(...) { grid.piece(..., op_scale=0.5, op_angle=45, default.units="in") }
g.p("tile_back", x=0.5+c(3,1,3,1), y=0.5+c(3,3,1,1))
g.p("tile_back", x=0.5+3, y=0.5+1, z=1/4+1/8)
g.p("tile_back", x=0.5+3, y=0.5+1, z=2/4+1/8)
g.p("die_face", suit=3, rank=5, x=1, y=1, z=1/4+1/4)
g.p("pawn_face", x=1, y=4, z=1/4+1/8, angle=90)
g.p("coin_back", x=3, y=4, z=1/4+1/16, angle=180)
g.p("coin_back", suit=4, x=3, y=4, z=1/4+1/8+1/16, angle=180)
g.p("coin_back", suit=2, x=3, y=1, z=3/4+1/8, angle=90)
```

`op_scale` and `op_angle` are the arguments that control the appearance of the oblique projection. `op_scale` determines how much to scale the length of the piece's edge along `op_angle`. An `op_scale` of `0.5` is commonly used in the cabinet projection, an `op_scale` of `1.0` is used in the cavalier projection, and an `op_scale` of `0.0` gives you the default "top view" orthographic projection. `op_angle` controls what angle the edges the pieces "go up" - it defaults to 45 degrees.

Depending on your preferences you may want to change up your pawns look and/or the color of your piece edges:

```
cfg <- pp_cfg(list(width.pawn=0.75, height.pawn=0.75, depth.pawn=1,
dm_text.pawn="", shape.pawn="convex6", invert_colors.pawn=TRUE,
edge_color.coin="tan", edge_color.tile="tan",
border_lex=2, border_color="black"))
g.p <- function(...) { grid.piece(..., op_scale=0.5, op_angle=45,
cfg=cfg, default.units="in") }
g.p("tile_back", x=0.5+c(3,1,3,1), y=0.5+c(3,3,1,1))
g.p("tile_back", x=0.5+3, y=0.5+1, z=1/4+1/8)
g.p("tile_back", x=0.5+3, y=0.5+1, z=2/4+1/8)
g.p("die_face", suit=3, rank=5, x=1, y=1, z=1/4+1/4)
g.p("pawn_face", x=1, y=4, z=1/4+1/2, angle=90)
g.p("coin_back", x=3, y=4, z=1/4+1/16, angle=180)
g.p("coin_back", suit=4, x=3, y=4, z=1/4+1/8+1/16, angle=180)
g.p("coin_back", suit=2, x=3, y=1, z=3/4+1/8, angle=90)
```

To directly make oblique projection graphics with `grid.piece` the programmer needs to figure out what height (the `z` argument) the various pieces are in relation to table and carefully arrange the `grid.piece` calls so the pieces on top and/or in front are drawn later. An easier way to make oblique projection graphics is to use `pmap_piece` with the helper function `op_transform` as `pmap_piece`'s `trans` argument. `op_transform` uses heuristics to make educated guesses about the height the game pieces are at and what order they should be drawn in:

```
df <- tibble::tibble(piece_side="tile_back",
x=c(2,2,2,4,6,6,4,2,5),
y=c(4,4,4,4,4,2,2,2,3))
pmap_piece(df, cfg=cfg, default.units="in",
op_angle=135, op_scale=0.5, trans=op_transform)
```

piecepackr can make an exact oblique projection under the same conditions it needs to do an exact orthographic projection (i.e. pieces all laid flat, no pyramids, pawns must be (genalized) meeple pawns laid face up or down) although it won't attempt to properly draw any visible "side" die faces.

## Mixed Projections

Technically since each `grid.piece` function can have its own `op_scale` and `op_angle` arguments one can mix and match projections in a single diagram:

```
cfg <- pp_cfg(list(edge_color.tile="black", border_lex=3))
draw_3tiles <- function(x, y, op_angle) {
grid.piece("tile_back", x=x, y=y, z=(1:3)/4-1/8,
op_scale=0.5, op_angle=op_angle, cfg=cfg, default.units="in")
}
draw_3tiles(2, 5, 180)
draw_3tiles(8, 5, 0)
draw_3tiles(5, 2, -90)
draw_3tiles(5, 8, 90)
draw_3tiles(8, 8, 45)
draw_3tiles(2, 8, 135)
draw_3tiles(2, 2, 225)
draw_3tiles(8, 2, -45)
```

## Pyramids

Currently piecepackr doesn't attempt to properly do an orthographic projection of laid down pyramids nor oblique projections of laid down and standing pyramids:

```
g.p <- function(...) { grid.piece(..., default.units="in") }
g.p("tile_back", x=2, y=2)
g.p("pyramid_top", x=2, y=2, suit=3, rank=3)
g.p("pyramid_face", x=2, y=3, suit=3, rank=3)
g.p("pyramid_left", x=1, y=2, suit=3, rank=3, angle=90)
g.p("pyramid_back", x=2, y=1, suit=3, rank=3, angle=180)
g.p("pyramid_right", x=3, y=2, suit=3, rank=3, angle=-90)
```

```
g.p <- function(..., z=1/4) { grid.piece(..., z=z, op_scale=0.5, default.units="in") }
g.p("tile_back", x=2, y=2, z=1/8)
g.p("pyramid_top", x=2, y=2, suit=3, rank=3)
g.p("pyramid_face", x=2, y=3, suit=3, rank=3)
g.p("pyramid_left", x=1, y=2, suit=3, rank=3, angle=90)
g.p("pyramid_back", x=2, y=1, suit=3, rank=3, angle=180)
g.p("pyramid_right", x=3, y=2, suit=3, rank=3, angle=-90)
```