# `Color.LED.RGBWW`

A five-channel **RGBWW** / **RGB+CCT** LED pixel — red, green,
blue plus a **warm white** and a **cool white** channel.

This is the pixel format used by the **WS2805** family and
several SK6812 variants. The warm and cool white LEDs can be
blended to reach any correlated colour temperature along the
line between them, giving much better white rendering than a
plain RGB pixel and more flexibility than single-temperature
RGBW.

All five channels — `:r`, `:g`, `:b`, `:ww`, `:cw` — are
**linear drive values in `[0.0, 1.0]`**, not sRGB-companded.

## Fields

* `:r`, `:g`, `:b` — linear drive for the red, green, blue LEDs.

* `:ww` — linear drive for the warm white LED.

* `:cw` — linear drive for the cool white LED.

* `:warm_temperature` — CCT (Kelvin) of the warm white LED.

* `:cool_temperature` — CCT (Kelvin) of the cool white LED.

* `:alpha` — optional alpha in `[0.0, 1.0]`, or `nil`.

## Extraction

`from_srgb/2` finds the target's CCT, clamps it to
`[warm_temperature, cool_temperature]`, picks a blend ratio
between the two whites that matches that CCT, synthesises a
single "mixed white" at that blend, runs the RGBW extraction
against it, and splits the resulting total white drive back
into `:ww` and `:cw` by the same ratio.

## Example

    iex> options = Color.LED.chip_options(:ws2805)
    iex> {:ok, target} = Color.new("#ffa500")
    iex> pixel = Color.LED.RGBWW.from_srgb(target, options)
    iex> match?(%Color.LED.RGBWW{warm_temperature: 3000, cool_temperature: 6500}, pixel)
    true

# `t`

```elixir
@type t() :: %Color.LED.RGBWW{
  alpha: Color.Types.alpha() | nil,
  b: float(),
  cool_temperature: number(),
  cw: float(),
  g: float(),
  r: float(),
  warm_temperature: number(),
  ww: float()
}
```

# `from_srgb`

```elixir
@spec from_srgb(
  Color.input(),
  keyword()
) :: t()
```

Builds an RGBWW pixel from an sRGB colour for a fixture whose
warm and cool white LEDs have the given colour temperatures.

### Arguments

* `srgb` is any value accepted by `Color.new/1`.

### Options

* `:warm_temperature` is the warm white LED's CCT in Kelvin.

* `:cool_temperature` is the cool white LED's CCT in Kelvin.

* `:chip` is an alternative to supplying the two temperatures
  explicitly: pass a chip atom from `Color.LED.chips/0` and the
  temperatures are looked up from `Color.LED.chip_options/1`.
  Only RGBWW chips are accepted.

### Returns

* A `Color.LED.RGBWW` struct.

### Examples

    iex> {:ok, white} = Color.new("#ffffff")
    iex> pixel = Color.LED.RGBWW.from_srgb(white, chip: :ws2805)
    iex> Float.round(pixel.ww + pixel.cw, 3) > 0.9
    true

# `to_srgb`

```elixir
@spec to_srgb(t()) :: {:ok, Color.SRGB.t()}
```

Simulates what an RGBWW pixel actually emits, as a companded
`Color.SRGB` struct.

### Arguments

* `pixel` is a `Color.LED.RGBWW` struct.

### Returns

* `{:ok, %Color.SRGB{}}`.

### Examples

    iex> {:ok, target} = Color.new("#ffa500")
    iex> pixel = Color.LED.RGBWW.from_srgb(target, chip: :ws2805)
    iex> {:ok, srgb} = Color.LED.RGBWW.to_srgb(pixel)
    iex> match?(%Color.SRGB{}, srgb)
    true

# `to_xyz`

```elixir
@spec to_xyz(t()) :: {:ok, Color.XYZ.t()} | {:error, Exception.t()}
```

Converts an RGBWW pixel to `Color.XYZ` by simulating its
emitted light.

### Arguments

* `pixel` is a `Color.LED.RGBWW` struct.

### Returns

* `{:ok, %Color.XYZ{}}` on success, `{:error, exception}` on
  failure.

### Examples

    iex> {:ok, target} = Color.new("#336699")
    iex> pixel = Color.LED.RGBWW.from_srgb(target, chip: :ws2805)
    iex> {:ok, xyz} = Color.LED.RGBWW.to_xyz(pixel)
    iex> match?(%Color.XYZ{}, xyz)
    true

---

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