Skip to content

layout

pleat.layout

Vertex position optimizations for half-edge graphs.

Provides helpers to find the rotation angle that minimizes the bounding-box height of a graph (useful before rendering elongated tilings) and a min_edge_length query used to set tolerances elsewhere.

angle_to_height

angle_to_height(G: 'GeometricHEG', angle: float) -> float

Return the height of the axis-aligned bounding box of G's border after rotating by angle.

Source code in pleat/layout.py
def angle_to_height(G: "GeometricHEG", angle: float) -> float:
    """Return the height of the axis-aligned bounding box of *G*'s border after rotating by *angle*."""
    border_positions = np.array([v["pos"] for v in G.border_vertex_iter()])
    rot_border_positions = border_positions @ np.array([[np.cos(angle)], [-np.sin(angle)]])
    return np.max(rot_border_positions) - np.min(rot_border_positions)

optimal_rotation

optimal_rotation(
    G: "GeometricHEG",
    angle_offset: float = 0,
    steps: int = 10000,
) -> float

Return the rotation angle minimising the bounding-box height of G's border.

Parameters:

Name Type Description Default
G 'GeometricHEG'

A graph with vertex pos attributes and a border iterator.

required
angle_offset float

Added to the optimal angle (e.g. pi/2 to optimise width instead).

0
steps int

Number of equally-spaced angles in [0, pi) to test.

10000
Source code in pleat/layout.py
def optimal_rotation(G: "GeometricHEG", angle_offset: float = 0, steps: int = 10000) -> float:
    """Return the rotation angle minimising the bounding-box height of *G*'s border.

    Args:
        G: A graph with vertex ``pos`` attributes and a border iterator.
        angle_offset: Added to the optimal angle (e.g. ``pi/2`` to optimise width instead).
        steps: Number of equally-spaced angles in ``[0, pi)`` to test.
    """
    border_positions = np.array([v["pos"] for v in G.border_vertex_iter()])

    def _angle_to_height(angle: float) -> float:
        rot_border_positions = border_positions @ np.array([[np.cos(angle)], [-np.sin(angle)]])
        return np.max(rot_border_positions) - np.min(rot_border_positions)

    angles = np.linspace(0, np.pi, steps)
    heights = [_angle_to_height(a) for a in angles]
    angle = angles[np.argmin(heights)] + angle_offset
    return angle

rotate_graph

rotate_graph(G: 'GeometricHEG', angle: float) -> None

Rotate every vertex of G by angle in place.

Source code in pleat/layout.py
def rotate_graph(G: "GeometricHEG", angle: float) -> None:
    """Rotate every vertex of *G* by *angle* in place."""
    ps = G.get_position_view(return_vertices=False)
    ps[:] = ps @ np.array([[np.cos(angle), np.sin(angle)], [-np.sin(angle), np.cos(angle)]])

optimize_rotation

optimize_rotation(
    G: "GeometricHEG", angle_offset: float = 0
) -> float

Rotate G in place to minimize its bounding-box height; return the applied angle.

Source code in pleat/layout.py
def optimize_rotation(G: "GeometricHEG", angle_offset: float = 0) -> float:
    """Rotate *G* in place to minimize its bounding-box height; return the applied angle."""
    angle = optimal_rotation(G, angle_offset)
    rotate_graph(G, angle)
    return angle

min_edge_length

min_edge_length(
    G: "GeometricHEG", include_border: bool = True
) -> float

Return the shortest edge length in G.

Parameters:

Name Type Description Default
G 'GeometricHEG'

A graph with vertex pos attributes.

required
include_border bool

If False, edges where either side is a border are skipped.

True
Source code in pleat/layout.py
def min_edge_length(G: "GeometricHEG", include_border: bool = True) -> float:
    """Return the shortest edge length in *G*.

    Args:
        G: A graph with vertex ``pos`` attributes.
        include_border: If False, edges where either side is a border are skipped.
    """
    edges = copy(G.halfedges)
    min_length = np.inf
    while edges:
        e = edges.pop()
        edges.remove(e.rev)
        if not include_border and (e.on_border() or e.rev.on_border()):
            continue
        min_length = min(((e.orig["pos"] - e.dest["pos"]) ** 2).sum(), min_length)
    return np.sqrt(min_length)