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.
Summary
Functions
Returns a point on the edge of a bounding rectangle.
Returns connector endpoints for a list of {from_id, to_id} edge pairs.
Converts a center-based position map to a map of bounding rectangles.
Functions
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}
@spec edge_endpoints( %{required(any()) => {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 == 0anddy == 0: returns centers (overlapping nodes degrade gracefully). - If
abs(dx) >= abs(dy): horizontal dominance. From-node uses:rightifdx > 0, else:left. To-node uses:leftifdx > 0, else:right. - Else: vertical dominance.
From-node uses
:bottomifdy > 0, else:top. To-node uses:topifdy > 0, else:bottom.
Options
:node_size— Either a{w, h}tuple or an arity-2 functionfn 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}}]
@spec rects( %{required(any()) => {float(), float()}}, keyword() ) :: %{required(any()) => {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 functionfn node_id, {cx, cy} -> {w, h} endfor 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}}