# `ExCodecs.Spatial.Bounds`
[🔗](https://github.com/AnimaLogica/codecs/blob/v0.2.4/lib/ex_codecs/spatial/bounds.ex#L1)

An axis-aligned bounding box.

## Fields

  * `:min_x`, `:min_y`, `:min_z` — `float()` lower Cartesian limits.
  * `:max_x`, `:max_y`, `:max_z` — `float()` upper Cartesian limits.

All six fields are enforced when constructing a literal struct. Their raw
`defstruct` defaults are `nil`, but `new/2` stores floats. Units are
application-defined and must match the bounded coordinates. Bounds are
inclusive, and this module does not require each minimum to be no greater
than its corresponding maximum.

## Example

    iex> alias ExCodecs.Spatial.Bounds
    iex> bounds = Bounds.new({-1, 0, 2}, {4, 5, 8})
    iex> {Bounds.center(bounds), Bounds.contains?(bounds, {0, 1, 3})}
    {{1.5, 2.5, 5.0}, true}

# `t`

```elixir
@type t() :: %ExCodecs.Spatial.Bounds{
  max_x: float(),
  max_y: float(),
  max_z: float(),
  min_x: float(),
  min_y: float(),
  min_z: float()
}
```

An inclusive axis-aligned bounding box. See the module documentation for
every field, defaults, units and conventions, and a construction example.

# `center`

```elixir
@spec center(t()) :: {float(), float(), float()}
```

Center point of the box.

## Arguments

  * `bounds` — `%Bounds{}`

## Returns

`{cx, cy, cz}` floats

## Raises

* `FunctionClauseError` if `bounds` is not a `%Bounds{}`.
* `ArithmeticError` if a manually constructed bounds has non-numeric fields.

## Examples

    iex> ExCodecs.Spatial.Bounds.center(ExCodecs.Spatial.Bounds.new({0, 0, 0}, {2, 2, 2}))
    {1.0, 1.0, 1.0}

# `contains?`

```elixir
@spec contains?(t(), {number(), number(), number()}) :: boolean()
```

Whether `{x,y,z}` lies inside or on the box.

## Arguments

  * `bounds` — `%Bounds{}`
  * `point` — `{number(), number(), number()}` in the bounds' coordinate
    units.

## Returns

boolean

## Raises

* `FunctionClauseError` unless the arguments are a `%Bounds{}` and a
  3-tuple. Elixir term ordering is otherwise used for non-numeric values
  supplied outside the documented types.

## Examples

    iex> b = ExCodecs.Spatial.Bounds.new({0, 0, 0}, {1, 1, 1})
    iex> ExCodecs.Spatial.Bounds.contains?(b, {0.5, 0.5, 0.5})
    true

# `from_points`

```elixir
@spec from_points(Enumerable.t()) :: t() | nil
```

Computes bounds from points or `{x,y,z}` tuples.

## Arguments

  * `points` — `Enumerable.t()` of maps/structs with numeric `:x`, `:y`,
    and `:z` fields, or numeric `{x, y, z}` tuples.

## Returns

`%Bounds{}` or `nil` if empty

## Raises

* `Protocol.UndefinedError` if `points` is not enumerable.
* `FunctionClauseError` if an element is not a supported point-like value.
* `ArithmeticError` if a map/struct coordinate is not numeric.

## Examples

    iex> ExCodecs.Spatial.Bounds.from_points([])
    nil
    iex> b = ExCodecs.Spatial.Bounds.from_points([{0, 0, 0}, {1, 1, 1}])
    iex> b.max_x
    1.0

# `new`

```elixir
@spec new(
  {number(), number(), number()},
  {number(), number(), number()}
) :: t()
```

Builds bounds from min and max corners.

## Arguments

  * `min` — numeric `{min_x, min_y, min_z}` lower corner in
    application-defined coordinate units.
  * `max` — numeric `{max_x, max_y, max_z}` upper corner in the same units.

## Returns

`%Bounds{}`

## Raises

* `FunctionClauseError` if either argument is not a 3-tuple.
* `ArithmeticError` if any component is not numeric.

## Examples

    iex> b = ExCodecs.Spatial.Bounds.new({0, 0, 0}, {1, 2, 3})
    iex> b.max_z
    3.0

# `size`

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

Extents `{dx, dy, dz}`.

## Arguments

  * `bounds` — `%Bounds{}`

## Returns

`{float(), float(), float()}`

## Raises

* `FunctionClauseError` if `bounds` is not a `%Bounds{}`.
* `ArithmeticError` if a manually constructed bounds has non-numeric fields.

## Examples

    iex> ExCodecs.Spatial.Bounds.size(ExCodecs.Spatial.Bounds.new({0, 0, 0}, {1, 2, 3}))
    {1.0, 2.0, 3.0}

---

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