---
title: "Scene mapping: from tape measurements to a to-scale diagram"
output:
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Scene mapping: from tape measurements to a to-scale diagram}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5.5,
                      dpi = 96, out.width = "100%")
# on Linux the default X11 png device cannot draw semi-transparent fills; use cairo there
if (Sys.info()[["sysname"]] == "Linux" && isTRUE(capabilities("cairo"))) knitr::opts_chunk$set(dev.args = list(png = list(type = "cairo")))
library(forensicR)
library(ggplot2)
```

The rough sketch you draw at the scene is the legal record. What forensicR
adds is the step after it: converting the numbers on that sketch into one
consistent coordinate frame, checking that they agree, and drawing the
to-scale plan and elevations from them.

## Conventions

- **Units**: anything, as long as everything uses the same one. The plots
  label axes with the `units` argument.
- **Frame**: x increases to the east, y to the north, z upwards from the
  floor. Pick a fixed origin, normally a room corner, and write it on the
  rough sketch.
- **Azimuth**: degrees clockwise from north. North is 0, east is 90.
- **Left and right of a wall** always mean *as seen standing inside the
  room facing that wall*.

## The three measurement methods

```{r methods-figure, echo = FALSE, fig.height = 3.2}
mk <- function(title, layers) {
  ggplot() + layers +
    coord_equal(xlim = c(-0.3, 4.3), ylim = c(-0.3, 3.3), expand = FALSE) +
    labs(title = title, x = NULL, y = NULL) +
    theme_minimal(base_size = 10) + theme(panel.grid = element_blank(), axis.text = element_blank())
}
room <- annotate("rect", xmin = 0, xmax = 4, ymin = 0, ymax = 3, fill = NA, color = "grey30", linewidth = 1)
pt <- annotate("point", x = 2.6, y = 1.8, size = 2.5)
p1 <- mk("Baseline", list(room,
  annotate("segment", x = 0, xend = 4, y = 0, yend = 0, color = "steelblue", linewidth = 1.2),
  annotate("segment", x = 0, xend = 2.6, y = 0.08, yend = 0.08, arrow = arrow(length = unit(2, "mm"), ends = "both"), color = "grey40"),
  annotate("text", x = 1.3, y = 0.25, label = "along", size = 3),
  annotate("segment", x = 2.6, xend = 2.6, y = 0, yend = 1.8, linetype = 2, color = "grey40"),
  annotate("text", x = 2.85, y = 0.9, label = "offset", size = 3, angle = 90), pt,
  annotate("text", x = 0, y = -0.15, label = "A", size = 3), annotate("text", x = 4, y = -0.15, label = "B", size = 3)))
p2 <- mk("Triangulation", list(room,
  annotate("segment", x = 0, xend = 2.6, y = 0, yend = 1.8, linetype = 2, color = "grey40"),
  annotate("segment", x = 4, xend = 2.6, y = 0, yend = 1.8, linetype = 2, color = "grey40"),
  annotate("text", x = 1.15, y = 1.05, label = "d1", size = 3), annotate("text", x = 3.45, y = 1.05, label = "d2", size = 3),
  annotate("point", x = c(0, 4), y = c(0, 0), shape = 15, color = "steelblue", size = 2.5), pt,
  annotate("text", x = 0, y = -0.15, label = "p1", size = 3), annotate("text", x = 4, y = -0.15, label = "p2", size = 3)))
p3 <- mk("Polar (total station)", list(room,
  annotate("point", x = 1, y = 0.5, shape = 17, color = "steelblue", size = 3),
  annotate("segment", x = 1, xend = 1, y = 0.5, yend = 2.2, color = "grey60"),
  annotate("text", x = 1, y = 2.35, label = "N", size = 3),
  annotate("segment", x = 1, xend = 2.6, y = 0.5, yend = 1.8, color = "grey40"),
  annotate("text", x = 1.55, y = 1.35, label = "distance", size = 3, angle = 39),
  annotate("path", x = 1 + 0.5 * sin(seq(0, 0.89, length.out = 20)), y = 0.5 + 0.5 * cos(seq(0, 0.89, length.out = 20)), color = "grey40"),
  annotate("text", x = 1.35, y = 1.1, label = "az", size = 3), pt))
gridExtra_available <- requireNamespace("gridExtra", quietly = TRUE)
if (gridExtra_available) gridExtra::grid.arrange(p1, p2, p3, nrow = 1) else print(p1)
```

### Baseline (rectangular coordinates)

Fix a line between two known points, usually two wall corners. For each
item record the distance *along* the line from the origin and the
perpendicular *offset*. Offsets are positive to the **left** when walking
from the origin toward the end point.

```{r baseline}
coords_baseline(along = c(1.2, 3.5), offset = c(0.8, 2.1),
                origin = c(0, 0), end = c(6, 0), id = c("A", "B"))
```

The baseline does not have to be along an axis. Any two known points work,
and the conversion handles the rotation:

```{r baseline-rot}
coords_baseline(along = 2, offset = 1, origin = c(1, 1), end = c(1, 5))
```

### Triangulation

Two distances from two fixed points. Two mirror-image solutions exist;
`side` picks the one to the left or right of the directed line from `p1`
to `p2`. If the distances cannot meet you get `NA` and a warning.

```{r tri}
coords_triangulation(d1 = c(3, 4), d2 = c(4, 5), p1 = c(0, 0), p2 = c(5, 0), side = "left")
coords_triangulation(d1 = 1, d2 = 1, p1 = c(0, 0), p2 = c(5, 0))
```

### Polar

Distance and azimuth from an instrument position, for total stations or
compass-and-tape work.

```{r polar}
coords_polar(distance = c(3.6, 5.0), azimuth = c(15, 330), station = c(3, -2))
```

### Checking measurements against each other

The most useful habit: measure a few points by two methods and compare.
Disagreement beyond a few centimeters means a reading or a reference point
is wrong.

```{r crosscheck}
a <- coords_baseline(along = 2.6, offset = 1.8, end = c(4, 0), id = "X (baseline)")
b <- coords_triangulation(d1 = sqrt(2.6^2 + 1.8^2), d2 = sqrt(1.4^2 + 1.8^2),
                          p1 = c(0, 0), p2 = c(4, 0), id = "X (triangulation)")
sqrt((a$x - b$x)^2 + (a$y - b$y)^2)      # closure error
```

## Height and type

Every point can carry a height `z` (a defect in a wall, a stain on a door)
and a `type`: `"evidence"` (default), `"defect"` or `"bloodstain"`. The
type only changes how the point is drawn.

```{r ztype}
coords_polar(distance = 3.6, azimuth = 15, station = c(3, -2),
             id = "6 Bullet defect", z = 1.35, type = "defect")
```

## Walls, doors, windows

`room_rect()` is the quick way to get a rectangle. Any room shape can be
described directly as polylines with columns `x`, `y`, `group` and `type`
(`"wall"`, `"door"` or `"window"`), so an L-shaped room is just a longer
polyline.

```{r walls}
walls <- rbind(
  room_rect(0, 0, 6, 5),
  opening(2.5, 0, 3.5, 0, type = "door"),
  opening(0, 2.0, 0, 3.2, type = "window")
)
lshape <- tibble::tibble(x = c(0, 8, 8, 4, 4, 0, 0), y = c(0, 0, 3, 3, 5, 5, 0),
                         group = "room", type = "wall")
plot_scene(coords_polar(1, 45, id = "1"), walls = lshape) + ggtitle("A non-rectangular room")
```

## Furniture

Objects are footprints with heights. Three ways to place them, matching how
they are measured:

```{r furniture}
furn <- rbind(
  along_wall(walls, "north", from = 3.4, length = 2.1, depth = 0.9, id = "Sofa", type = "sofa"),
  along_wall(walls, "east",  from = 0.3, length = 1.2, depth = 0.6, id = "Bookcase", type = "bookcase"),
  furniture("Table", "table", x = 3.0, y = 2.0, width = 1.2, depth = 0.8, angle = 15),
  furniture_from_corners("TV stand", "tv_stand", p1 = c(0.2, 0.2), p2 = c(1.4, 0.6)),
  furniture("Stool", "chair", x = 1.6, y = 3.6, diameter = 0.4, shape = "circle", color = "#8e6bbf"),
  furniture("Victim", "person_lying", x = 2.2, y = 1.0, width = 1.7, depth = 0.5, angle = 20)
)
furniture_table(furn)
```

Use `measured = FALSE` for anything whose size was estimated rather than
measured: it is drawn dashed and flagged in the report table. `color` and
`alpha` (opacity, default 0.45) are per object; `furniture_alpha` in any
plotting function overrides them all at once.

## Plan view

```{r plan}
pts <- rbind(
  coords_baseline(c(1.5, 3.2, 4.8), c(0.9, 2.1, 0.4), end = c(6, 0),
                  id = c("1 Cartridge case", "2 Cartridge case", "3 Firearm")),
  coords_polar(3.6, 15, station = c(3, -2), id = "6 Bullet defect", z = 1.35, type = "defect")
)
plot_scene(pts, walls = walls, furniture = furn)
```

The result is a ggplot object, so you can add a title, a north arrow or
notes with ordinary ggplot2 code.

## Elevations

Each wall face-on, with the heights of what is on or near it. Use
`wall_from_room()` to get a wall of a rectangular room already ordered
left-to-right as seen from inside, or pass any segment `c(x1, y1, x2, y2)`.

```{r elevations, fig.height = 3.8}
plot_wall_elevation(wall_from_room(walls, "north"), pts, walls = walls, furniture = furn)
plot_wall_elevation(wall_from_room(walls, "west"), pts, walls = walls, furniture = furn)
```

Items are selected by their perpendicular distance to the wall (`tol`,
default 0.15) and objects by `tol_furniture` (default 0.35), so a bookcase
standing near a corner appears on both adjacent walls, as it would in the
room.

## Exporting

Points and footprints are plain data frames. Write them out for a CAD or
diagramming package with `write.csv()`:

```{r export, eval = FALSE}
write.csv(pts, "scene-points.csv", row.names = FALSE)
write.csv(furniture_footprint(furn), "furniture-footprints.csv", row.names = FALSE)
```
