# `Quiddity.Frame`
[🔗](https://github.com/d-led/ex.quiddity/blob/main/lib/quiddity/frame.ex#L1)

One camera frame of a part: what each pixel landed on.

Four answers per pixel, all of them exact, because the ray met the surface
rather than a depth buffer that then had to be un-projected:

  * the point on the part (millimetres, the file's own coordinates);
  * the outward normal there — and for a pixel that met nothing, the ray
    stopped at the far plane and the normal points back at the sensor, which is
    what "a surface would face me here" means;
  * the face that claimed it (`nil` for a pixel that met nothing);
  * the distance along the ray.

Points and normals are kept as the flat binaries the engine answered; a caller
asks for `rows/1` when it wants lists, and pays for the walk then rather than
on every frame.

# `t`

```elixir
@type t() :: %Quiddity.Frame{
  camera: Quiddity.Camera.t(),
  depth: binary(),
  faces: binary(),
  height: pos_integer(),
  normals: binary(),
  points: binary(),
  width: pos_integer()
}
```

# `centre`

```elixir
@spec centre(t()) :: {[float()], [float()], non_neg_integer() | nil}
```

The centre pixel's row: `{point, normal, face}`.

A patch sensor reads its location from the middle pixel, which is why this is
the one pixel with a name.

# `coverage`

```elixir
@spec coverage(t()) :: float()
```

The share of pixels that met the part, from 0 to 1.

# `depth_range`

```elixir
@spec depth_range(t()) :: {float() | :none, float() | :none}
```

The distance to the nearest and furthest pixel that met the part.

`{:none, :none}` for a frame that met nothing, which is a different answer from
a frame whose surface is at zero distance.

# `depths`

```elixir
@spec depths(t()) :: [float()]
```

Every pixel's distance along its ray, in millimetres.

# `face_ids`

```elixir
@spec face_ids(t()) :: [non_neg_integer() | nil]
```

Every pixel's face, and `nil` where the ray met nothing.

# `families`

```elixir
@spec families(t(), Quiddity.Recognition.t()) :: [
  {String.t() | :body | :unexplained, pos_integer()}
]
```

The families present in the frame, and how many pixels each covers.

This is the answer to "what am I looking at": the frame's faces are looked up in
the part's recognition, so a patch on a counterbore says so, and a patch on
plain stock says nothing at all.

# `hits`

```elixir
@spec hits(t()) :: non_neg_integer()
```

How many pixels met the part.

# `new`

```elixir
@spec new(map(), Quiddity.Camera.t()) :: t()
```

The frame built from what the engine answered.

`points`, `normals` and `faces` are the engine's own binaries: `3 * w * h`
little-endian f64, `3 * w * h` f64, `w * h` u32 — and `depth`, `w * h` f64.

# `normals`

```elixir
@spec normals(t()) :: [[float()]]
```

Every pixel's outward normal.

# `point`

```elixir
@spec point(t(), non_neg_integer()) :: [float()]
```

The pixel's point, in millimetres.

# `points`

```elixir
@spec points(t()) :: [[float()]]
```

Every pixel's point, in millimetres.

# `rgba`

```elixir
@spec rgba(t(), keyword()) :: [[non_neg_integer()]]
```

A shaded image of the frame, `[r, g, b, a]` per pixel, 0 to 255.

A B-rep has no colour: quiddity reads geometry, and a STEP file that carries no
appearance is grey. So this is *shading* — one light from the upper left of the
camera — and it is honest about what it is: a `hsv` feature read from these
pixels says how the surface turns, not what it is painted.

# `rows`

```elixir
@spec rows(t()) :: [{[float()], [float()], non_neg_integer() | nil}]
```

One `{[x, y, z], [nx, ny, nz], face}` row per pixel, row-major.

# `semantic_3d`

```elixir
@spec semantic_3d(t(), keyword()) :: [[float()]]
```

The rows a sensor observation carries: `[x, y, z, semantic_id]` per pixel.

A pixel that met the part carries `semantic_id`; a pixel that met nothing
carries `0.0`, the background id. A sensor model reads that fourth column to
decide which pixels are on the object, and what fraction of the patch it
covers.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
