# `FrenchCurve.Geometry`
[🔗](https://github.com/jaman/french_curve/blob/v0.2.0/lib/french_curve/geometry.ex#L1)

Converts between terminal cells and pixels.

Sizing a raster to a region of the screen needs the terminal's cell size in pixels,
which only the terminal knows. `cell_size_query/0` returns the escape sequence that asks
for it and `parse_cell_size/1` reads the answer; `detect_cell_size/1` does both through a
callback you supply. When the terminal will not say, `default_cell_px/0` stands in.

Sizes are `{width, height}` in pixels throughout, and regions are `{cols, rows}` in
cells.

    cell_px =
      case FrenchCurve.Geometry.detect_cell_size(&query_terminal/1) do
        {:ok, size} -> size
        :error -> FrenchCurve.Geometry.default_cell_px()
      end

    {width, height} = FrenchCurve.Geometry.pixels_for_cells({40, 12}, cell_px)

# `cell_px`

```elixir
@type cell_px() :: {pos_integer(), pos_integer()}
```

# `region`

```elixir
@type region() :: {pos_integer(), pos_integer()}
```

# `braille_cell_px`

```elixir
@spec braille_cell_px() :: cell_px()
```

Returns the pixels one braille cell covers, `{2, 4}`.

Fixed by the braille dot layout, not by the terminal: `FrenchCurve.Backend.Braille` maps
a 2-by-4 block of raster pixels onto the eight dots of one character.

# `cell_size_query`

```elixir
@spec cell_size_query() :: binary()
```

Returns the escape sequence that asks the terminal for its cell size in pixels.

Write it to the terminal and read the reply back with `parse_cell_size/1`.

# `default_cell_px`

```elixir
@spec default_cell_px() :: cell_px()
```

Returns the assumed cell size, `{10, 20}` pixels, for terminals that do not report one.

# `detect_cell_size`

```elixir
@spec detect_cell_size((binary() -&gt; binary() | nil)) :: {:ok, cell_px()} | :error
```

Asks the terminal for its cell size through `query_io` and parses the reply.

`query_io` is a one-argument function; it is called with `cell_size_query/0` and must
write that to the terminal and return the terminal's raw reply as a binary, or any
non-binary if there was none. Returns `{:ok, {width, height}}` in pixels, or `:error`
when no reply came back or it could not be parsed.

# `parse_cell_size`

```elixir
@spec parse_cell_size(binary()) :: {:ok, cell_px()} | :error
```

Parses a terminal's reply to `cell_size_query/0`.

Expects a binary containing `\e[6;<height>;<width>t`; surrounding bytes are ignored.
Returns `{:ok, {width, height}}` in pixels, or `:error` if the reply does not match.
Note that the terminal reports height before width and this returns width first.

# `pixels_for_cells`

```elixir
@spec pixels_for_cells(region(), cell_px() | :unknown) ::
  {pos_integer(), pos_integer()}
```

Returns the pixel size `{width, height}` of a `{cols, rows}` region of cells.

Multiplies each dimension by the corresponding cell dimension. Pass `:unknown` as the
cell size to use `default_cell_px/0`.

---

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