Current section

Files

Jump to
identikon lib identicon image builder.ex
Raw

lib/identicon/image/builder.ex

defmodule Identicon.Image.Builder do
@moduledoc """
Updates fields `indexes` and `squares` of an image struct.
"""
alias Identicon.Image
@typedoc "Image square index"
@type square_index :: non_neg_integer
@doc """
Updates field `indexes` of image struct `image`.
"""
@spec derive_indexes(Image.t()) :: Image.t()
def derive_indexes(
%Image{
dimension: dimension,
chunk_size: chunk_size,
bytes: bytes
} = image
) do
indexes =
bytes
|> Enum.chunk_every(chunk_size)
|> Enum.map(&mirror_chunk(&1, dimension))
|> List.flatten()
|> Enum.with_index()
|> even_byte_indexes()
# Indexes of colored squares...
put_in(image.indexes, indexes)
end
@doc """
Updates field `squares` of image struct `image`.
"""
@spec derive_squares(Image.t()) :: Image.t()
def derive_squares(
%Image{
dimension: dimension,
square_size: square_size,
indexes: indexes
} = image
) do
squares =
Enum.map(indexes, fn index ->
x = rem(index, dimension) * square_size
y = div(index, dimension) * square_size
# Corners (top-left and bottom-right) of colored squares...
{{x, y}, {x + square_size - 1, y + square_size - 1}}
end)
put_in(image.squares, squares)
end
## Private functions
@spec mirror_chunk([byte], pos_integer) :: [[byte] | byte]
defp mirror_chunk(chunk, dimension) when rem(dimension, 2) == 0,
# E.g. for dimension 8 => [[1, 2, 3, 4], 4, 3, 2, 1]
do: [chunk | Enum.reverse(chunk)]
defp mirror_chunk(chunk, _dimension),
# E.g. for dimension 7 => [[1, 2, 3, 4], 3, 2, 1]
do: [chunk | Enum.drop(chunk, -1) |> Enum.reverse()]
@spec even_byte_indexes([{byte, square_index}]) :: [square_index]
defp even_byte_indexes(tuples) do
for {byte, index} <- tuples, rem(byte, 2) == 0, do: index
end
end