# `Color.Harmony`

Color harmony helpers — rotations and named combinations on the hue
circle.

All helpers operate in **Oklch** by default, which gives visually
consistent rotations across the hue wheel. Pass `:in` to select a
different cylindrical space (`Color.LCHab`, `Color.LCHuv`,
`Color.HSL`, `Color.HSV`).

Every function accepts any color accepted by `Color.new/1` and
returns a list of `Color.SRGB` structs, ordered starting with the
input color.

# `analogous`

```elixir
@spec analogous(
  Color.input(),
  keyword()
) :: {:ok, [Color.SRGB.t()]} | {:error, Exception.t()}
```

Returns the three-color analogous set (`[-30°, 0, +30°]`).

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `options` is the same as for `rotate_hue/3`, plus `:spread`
  (degrees, default `30`).

### Returns

* `{:ok, [%Color.SRGB{}, %Color.SRGB{}, %Color.SRGB{}]}` ordered
  `[anchor, anchor - spread, anchor + spread]`.

# `complementary`

```elixir
@spec complementary(
  Color.input(),
  keyword()
) :: {:ok, [Color.SRGB.t()]} | {:error, Exception.t()}
```

Returns the two-color complementary pair (`[input, +180°]`).

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `options` is the same as for `rotate_hue/3`.

### Returns

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

# `rotate_hue`

```elixir
@spec rotate_hue(Color.input(), number(), keyword()) ::
  {:ok, Color.SRGB.t()} | {:error, Exception.t()}
```

Rotates a color's hue by `degrees`.

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `degrees` is the rotation in degrees. Positive is
  counter-clockwise on the hue wheel.

* `options` is a keyword list.

### Options

* `:in` is the cylindrical color space to rotate in. Defaults to
  `Color.Oklch`.

### Returns

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

### Examples

    iex> {:ok, rotated} = Color.Harmony.rotate_hue("red", 120)
    iex> hex = Color.SRGB.to_hex(rotated)
    iex> String.starts_with?(hex, "#")
    true

# `split_complementary`

```elixir
@spec split_complementary(
  Color.input(),
  keyword()
) :: {:ok, [Color.SRGB.t()]} | {:error, Exception.t()}
```

Returns the three-color split-complementary set
(`[0, 180 − spread, 180 + spread]`).

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `options` is the same as for `rotate_hue/3`, plus `:spread`
  (degrees, default `30`).

### Returns

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

# `tetradic`

```elixir
@spec tetradic(
  Color.input(),
  keyword()
) :: {:ok, [Color.SRGB.t()]} | {:error, Exception.t()}
```

Returns the four-color tetradic set (`[0, +90°, +180°, +270°]`).

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `options` is the same as for `rotate_hue/3`.

### Returns

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

# `triadic`

```elixir
@spec triadic(
  Color.input(),
  keyword()
) :: {:ok, [Color.SRGB.t()]} | {:error, Exception.t()}
```

Returns the three-color triadic set (`[0, +120°, +240°]`).

### Arguments

* `color` is any color accepted by `Color.new/1`.

* `options` is the same as for `rotate_hue/3`.

### Returns

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

---

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