# `Yog.Layout.Geometry`
[🔗](https://github.com/code-shoily/yog_ex/blob/v1.0.0/lib/yog/layout/geometry.ex#L1)

Geometry helpers for converting center-based node positions to bounding rectangles,
computing anchor points on rectangle edges, and deriving connector endpoints for edges.

These utilities are designed to support rendering pipelines that need to convert
abstract layout coordinates into concrete geometric primitives for drawing.

# `anchor`

```elixir
@spec anchor({float(), float(), float(), float()}, atom()) :: {float(), float()}
```

Returns a point on the edge of a bounding rectangle.

The rectangle is given as `{x, y, w, h}` where `x` and `y` are the top-left corner.

## Directions

  * `:top` — top-center `{x + w/2, y}`
  * `:bottom` — bottom-center `{x + w/2, y + h}`
  * `:left` — left-center `{x, y + h/2}`
  * `:right` — right-center `{x + w, y + h/2}`
  * `:top_left` — `{x, y}`
  * `:top_right` — `{x + w, y}`
  * `:bottom_left` — `{x, y + h}`
  * `:bottom_right` — `{x + w, y + h}`
  * `:center` — `{x + w/2, y + h/2}`

## Examples

    iex> Yog.Layout.Geometry.anchor({10.0, 20.0, 4.0, 2.0}, :right)
    {14.0, 21.0}

    iex> Yog.Layout.Geometry.anchor({10.0, 20.0, 4.0, 2.0}, :center)
    {12.0, 21.0}

    iex> Yog.Layout.Geometry.anchor({10.0, 20.0, 4.0, 2.0}, :top)
    {12.0, 20.0}

    iex> Yog.Layout.Geometry.anchor({10.0, 20.0, 4.0, 2.0}, :bottom)
    {12.0, 22.0}

    iex> Yog.Layout.Geometry.anchor({10.0, 20.0, 4.0, 2.0}, :left)
    {10.0, 21.0}

# `edge_endpoints`

```elixir
@spec edge_endpoints(
  %{required(any()) =&gt; {float(), float()}},
  [{any(), any()}],
  keyword()
) :: [{{float(), float()}, {float(), float()}}]
```

Returns connector endpoints for a list of `{from_id, to_id}` edge pairs.

Without `:node_size`, endpoints are the node centers. With `:node_size`, endpoints
are clipped to the closest cardinal side midpoint of each node's bounding rectangle.

## Cardinal side selection

Given `dx = to_cx - from_cx` and `dy = to_cy - from_cy`:

  * If `dx == 0` and `dy == 0`: returns centers (overlapping nodes degrade gracefully).
  * If `abs(dx) >= abs(dy)`: horizontal dominance.
    From-node uses `:right` if `dx > 0`, else `:left`.
    To-node uses `:left` if `dx > 0`, else `:right`.
  * Else: vertical dominance.
    From-node uses `:bottom` if `dy > 0`, else `:top`.
    To-node uses `:top` if `dy > 0`, else `:bottom`.

## Options

  * `:node_size` — Either a `{w, h}` tuple or an arity-2 function
    `fn node_id, {cx, cy} -> {w, h} end`. When provided, endpoints are clipped
    to rect edges; otherwise raw centers are returned.

## Examples

    iex> Yog.Layout.Geometry.edge_endpoints(
    ...>   %{a: {0.0, 0.0}, b: {10.0, 0.0}},
    ...>   [{:a, :b}],
    ...>   node_size: {2.0, 2.0}
    ...> )
    [{{1.0, 0.0}, {9.0, 0.0}}]

# `rects`

```elixir
@spec rects(
  %{required(any()) =&gt; {float(), float()}},
  keyword()
) :: %{required(any()) =&gt; {float(), float(), float(), float()}}
```

Converts a center-based position map to a map of bounding rectangles.

Each entry `node_id => {cx, cy}` becomes `node_id => {left_x, top_y, width, height}`,
where `left_x = cx - w/2` and `top_y = cy - h/2`.

## Options

  * `:size` - Either a `{w, h}` tuple applied uniformly to all nodes, or an arity-2
    function `fn node_id, {cx, cy} -> {w, h} end` for per-node sizes.
    Defaults to `{1.0, 1.0}`.

## Examples

    iex> Yog.Layout.Geometry.rects(%{a: {10.0, 20.0}}, size: {4.0, 2.0})
    %{a: {8.0, 19.0, 4.0, 2.0}}

---

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