2024-05-14 21:58:42 +02:00
|
|
|
defmodule Liliform.Colorhash do
|
|
|
|
@moduledoc """
|
|
|
|
Given a string returns HSL color.
|
|
|
|
"""
|
|
|
|
alias Freedive.Features
|
2024-05-15 20:53:00 +02:00
|
|
|
require Logger
|
2024-05-14 21:58:42 +02:00
|
|
|
|
|
|
|
@seed "zeni"
|
|
|
|
@default_color {0, 0, 0}
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Given a string returns HSL color as a CSS string.
|
|
|
|
"""
|
|
|
|
def hsl(input) do
|
|
|
|
{hue, saturation, lightness} = hsl(input, raw: true)
|
|
|
|
"hsl(#{hue}, #{saturation}%, #{lightness}%)"
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Given a string returns HSL color.
|
|
|
|
"""
|
|
|
|
def hsl(input, raw: true) do
|
|
|
|
if Features.enabled?(:colorhash) do
|
|
|
|
input = String.downcase(input) <> @seed
|
|
|
|
hash = :erlang.phash2(input, 2_147_483_647)
|
|
|
|
|
|
|
|
hue = calculate_hue(hash)
|
|
|
|
saturation = 80 + rem(hash, 21)
|
|
|
|
lightness = 30 + rem(hash, 31)
|
|
|
|
|
|
|
|
{hue, saturation, lightness}
|
|
|
|
else
|
|
|
|
@default_color
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Calculates hue avoiding low contrast colors.
|
|
|
|
"""
|
|
|
|
def calculate_hue(hash) do
|
|
|
|
base_hue = rem(hash, 360)
|
|
|
|
|
|
|
|
# Ranges to exclude (yellow to light green, cyan)
|
|
|
|
excluded_ranges = [
|
|
|
|
# yellow
|
|
|
|
{40, 80},
|
|
|
|
# cyan
|
|
|
|
{175, 185}
|
|
|
|
]
|
|
|
|
|
|
|
|
# Adjust the hue to skip over excluded ranges
|
|
|
|
Enum.reduce(excluded_ranges, base_hue, fn {start, stop}, acc_hue ->
|
|
|
|
if acc_hue >= start and acc_hue <= stop, do: stop + (acc_hue - start + 1), else: acc_hue
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|