# `Isotope.NoiseMap`
[🔗](https://codeberg.org/sickday/Isotope118/src/branch/main/lib/noise_map.ex#L1)

A compact representation of a 2D noise map backed by a flat binary of
little-endian `f32` values in row-major order.

## Memory Layout

The `data` field is a binary where each value is a 32-bit float in
little-endian byte order. Values are stored row by row (row-major).
A 100x100 map uses exactly 40,000 bytes (~40 KB), compared to ~3.2 MB
for the equivalent nested Elixir list.

## Accessing Values

    {:ok, noise} = Isotope.Noise.new()
    nm = Isotope.Noise.noise_map(noise, {100, 100})

    # Single value at column 10, row 5
    Isotope.NoiseMap.get(nm, 10, 5)

    # Entire row as a list of floats
    Isotope.NoiseMap.row(nm, 0)

    # Convert to nested list (for compatibility or serialization)
    Isotope.NoiseMap.to_list(nm)

    # Dimensions
    {width, height} = Isotope.NoiseMap.size(nm)

## Enumerable

The `Enumerable` protocol is implemented, iterating over rows (each row
is a `[float()]`). This means standard `Enum` functions work directly:

    # Average value per row
    Enum.map(nm, fn row -> Enum.sum(row) / length(row) end)

    # Find the row with the highest peak
    Enum.max_by(nm, fn row -> Enum.max(row) end)

    # First 5 rows
    Enum.take(nm, 5)

## Migration from `[[float()]]`

If you have code that expects nested lists, use `Isotope.NoiseMap.to_list/1`:

    nm = Isotope.Noise.noise_map(noise, {100, 100})
    nested_list = Isotope.NoiseMap.to_list(nm)

# `t`

```elixir
@type t() :: %Isotope.NoiseMap{
  data: binary(),
  height: non_neg_integer(),
  width: non_neg_integer()
}
```

# `get`

```elixir
@spec get(t(), non_neg_integer(), non_neg_integer()) :: float()
```

Returns the noise value at column `x`, row `y`.

Raises `ArgumentError` if the coordinates are out of bounds.

    iex> nm = %Isotope.NoiseMap{data: <<1.0::float-little-32, 2.0::float-little-32, 3.0::float-little-32, 4.0::float-little-32>>, width: 2, height: 2}
    iex> Isotope.NoiseMap.get(nm, 1, 0)
    2.0

# `row`

```elixir
@spec row(t(), non_neg_integer()) :: [float()]
```

Returns row `y` as a list of floats.

Raises `ArgumentError` if the row index is out of bounds.

    iex> nm = %Isotope.NoiseMap{data: <<1.0::float-little-32, 2.0::float-little-32, 3.0::float-little-32, 4.0::float-little-32>>, width: 2, height: 2}
    iex> Isotope.NoiseMap.row(nm, 0)
    [1.0, 2.0]

# `size`

```elixir
@spec size(t()) :: {non_neg_integer(), non_neg_integer()}
```

Returns `{width, height}` of the noise map.

    iex> nm = %Isotope.NoiseMap{data: <<0::128>>, width: 2, height: 2}
    iex> Isotope.NoiseMap.size(nm)
    {2, 2}

# `to_list`

```elixir
@spec to_list(t()) :: [[float()]]
```

Converts the noise map to a nested list `[[float()]]` (list of rows).

    iex> nm = %Isotope.NoiseMap{data: <<1.0::float-little-32, 2.0::float-little-32, 3.0::float-little-32, 4.0::float-little-32>>, width: 2, height: 2}
    iex> Isotope.NoiseMap.to_list(nm)
    [[1.0, 2.0], [3.0, 4.0]]

---

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