# `Ascii.Ink`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/ink.ex#L1)

Colour for a piece drawn in one ink: one colour for every character, or
several as a gradient across the picture, and colours for its parts.

Most pieces are text in one ink, the page's or the terminal's own. Given
an ink (`Ascii.new(piece, ink: ...)`), their frames come with colours,
so every renderer draws them as it draws a coloured piece: a canvas in the
browser, 24-bit or xterm-256 escapes in a terminal, and plain text where
colour cannot be shown (`mode: :mono`). The picture itself is unchanged,
character for character: the ink is laid over it, never drawn by the piece.

A coloured piece keeps its own palette; drawn in one ink (`color: false`)
it takes the ink like any other.

    {:ok, anim} = Ascii.new("big-text", ink: ["#ff5f6d", "#ffc371"])
    {:ok, anim} = Ascii.new("donut", ink: "#7fdbca")
    {:ok, anim} = Ascii.new("wave-text", ink: "#36d1dc,#5b86e5", gradient: :vertical)

Pieces that know their parts (a file tree's folders, a boot log's
statuses) take colours for those too, with `:parts`, over the ink or
without one: see `Ascii.Parts`.

## Colours

`#rrggbb` or `#rgb`, the `#` optional; several as a list or as one string,
comma-separated (as from a form or a command line), up to 16. One colour
is every character's. Several run evenly from the first to the last across
the whole picture, in a straight line between each and the next, as CSS's
`linear-gradient` does, so a piece whose text moves (a marquee, a wave)
moves through them.

## CSS variables

One ink, or a part's colour, can be a CSS custom property, so a piece on
a page takes the page's own colours, and follows them into dark mode:
`"var(--color-success)"`, or with a colour for where there is no CSS,
`"var(--color-success, #3fb950)"`. In HTML (`Ascii.Render.HTML`, the
Phoenix players, `Ascii.Render.SVG`) the variable is the colour; a
terminal draws the fallback, or its own ink without one (`resolve/1`).
Several inks run as a gradient, worked out here, so they must be colours.

## Gradients

  * `:horizontal` (the default) - left to right.
  * `:vertical` - top to bottom.
  * `:diagonal` - top left to bottom right, at 45° on screen, cells being
    as tall as the piece's `cell` says.

A gradient is 65 steps, whatever the picture's size, so its palette is
the same for every frame (a player sends it once) and a wide picture costs
few escapes in a terminal and few spans in HTML.

# `gradient`

```elixir
@type gradient() :: :horizontal | :vertical | :diagonal
```

Which way several colours run.

# `t`

```elixir
@type t() :: %Ascii.Ink{
  cells: binary() | nil,
  colors: [String.t(), ...] | nil,
  gradient: gradient(),
  index: %{optional(String.t()) =&gt; non_neg_integer() | :base} | nil,
  palette: [String.t() | nil] | nil,
  parts: Ascii.Parts.colors(),
  size: {pos_integer(), pos_integer(), 1 | 2} | nil
}
```

An ink: its colours, as `#rrggbb` or a CSS variable (`nil` for the
renderer's own), which way they run, and colours for parts (see `Ascii.Parts`). Made by
`cast/2` and `put_parts/2`; the rest is its layout for one size of frame,
filled in as it is needed.

# `cast`

```elixir
@spec cast(colors :: term(), gradient :: term()) ::
  {:ok, t() | nil} | Ascii.Options.error()
```

An ink from outside (code, LiveView params, a command line): its colours
and, for several, which way they run. No colours at all (`nil`, `""` or
`[]`) is no ink. Never creates an atom.

    iex> Ascii.Ink.cast("#F80")
    {:ok, %Ascii.Ink{colors: ["#ff8800"], gradient: :horizontal}}

    iex> Ascii.Ink.cast("#ff5f6d,#ffc371", "vertical")
    {:ok, %Ascii.Ink{colors: ["#ff5f6d", "#ffc371"], gradient: :vertical}}

    iex> Ascii.Ink.cast("red")
    {:error, {:invalid_option, :ink}}

# `color`

```elixir
@spec color(color :: term()) :: String.t() | nil
```

A colour from outside, as this library keeps it: `#rrggbb` (from
`#rrggbb` or `#rgb`, the `#` optional), or a CSS variable as
`var(--name)` or `var(--name, #rrggbb)`; `nil` if it is neither.

    iex> Ascii.Ink.color("F80")
    "#ff8800"
    iex> Ascii.Ink.color("var( --accent ,#FFF)")
    "var(--accent, #ffffff)"
    iex> Ascii.Ink.color("var(--x);color:red")
    nil

# `put_parts`

```elixir
@spec put_parts(ink :: t() | nil, parts :: Ascii.Parts.colors()) :: t() | nil
```

The ink with colours for parts (as `Ascii.Parts.cast/2` makes them),
laid over its own. Without an ink, the parts take theirs over the
renderer's own ink; without parts, the ink is as it was.

# `resolve`

```elixir
@spec resolve(color :: String.t() | nil) :: String.t() | nil
```

The `#rrggbb` to draw a colour in where CSS cannot be read (a terminal, a
framebuffer): the colour itself, a CSS variable's fallback, or `nil` for
the renderer's own ink.

    iex> Ascii.Ink.resolve("var(--ok, #3fb950)")
    "#3fb950"
    iex> Ascii.Ink.resolve("var(--ok)")
    nil

# `tint`

```elixir
@spec tint(frame :: Ascii.Frame.t(), ink :: t() | nil, parts :: Ascii.Parts.t() | nil) ::
  Ascii.Frame.t()
```

The frame in this ink: its text as it is, each cell given a colour, and no
ground behind it. With `parts`, the frame's runs of parts (see
`Ascii.Parts`), a part with a colour of its own takes it. A frame with
colours of its own, or no ink (`nil`), is left as it is.

---

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