Skip to content

Punctuation spiral

A text's punctuation and logical signs wound onto an Archimedean spiral.

lexograph.presets.punctuation_spiral.punctuation_spiral(text, *, turns=16.0, size_min=7.0, size_max=12.0, figsize=(9.0, 9.0))

Draw a text's punctuation and logical signs as a spiral plate.

Parameters:

Name Type Description Default
text str

The source text.

required
turns float

How many revolutions the spiral makes.

16.0
size_min float

Font size (points) of the innermost marks.

7.0
size_max float

Font size (points) of the outermost marks.

12.0
figsize tuple[float, float]

Figure size in inches.

(9.0, 9.0)

Returns:

Name Type Description
A Figure

class:matplotlib.figure.Figure with one axes, dark-themed. Never

Figure

calls show(), so it renders inline in Jupyter and saves with

Figure

fig.savefig(...).

Raises:

Type Description
ValueError

If text contains no punctuation or symbol marks.

Contract
  • Returns a Figure with exactly one axes.
  • Accent marks (logical/mathematical/Greek) are drawn larger and on top.

Examples:

>>> from lexograph import load_demo_text
>>> fig = punctuation_spiral(load_demo_text())
>>> type(fig).__name__
'Figure'
>>> len(fig.axes)
1
Source code in lexograph/presets/punctuation_spiral.py
def punctuation_spiral(
    text: str,
    *,
    turns: float = 16.0,
    size_min: float = 7.0,
    size_max: float = 12.0,
    figsize: tuple[float, float] = (9.0, 9.0),
) -> Figure:
    """Draw a text's punctuation and logical signs as a spiral plate.

    Args:
        text: The source text.
        turns: How many revolutions the spiral makes.
        size_min: Font size (points) of the innermost marks.
        size_max: Font size (points) of the outermost marks.
        figsize: Figure size in inches.

    Returns:
        A :class:`matplotlib.figure.Figure` with one axes, dark-themed. Never
        calls ``show()``, so it renders inline in Jupyter and saves with
        ``fig.savefig(...)``.

    Raises:
        ValueError: If ``text`` contains no punctuation or symbol marks.

    Contract:
        - Returns a Figure with exactly one axes.
        - Accent marks (logical/mathematical/Greek) are drawn larger and on top.

    Examples:
        >>> from lexograph import load_demo_text
        >>> fig = punctuation_spiral(load_demo_text())
        >>> type(fig).__name__
        'Figure'
        >>> len(fig.axes)
        1
    """
    marks = _marks(text)
    if not marks:
        msg = "text contains no punctuation or symbol marks to plot"
        raise ValueError(msg)

    n = len(marks)
    coords = spiral_layout(n, turns=turns, r0=1.0, r_max=10.0)
    angles = tangent_angles(coords)
    # Marks grow linearly in size from the centre outward.
    sizes = np.linspace(size_min, size_max, n)

    fig = Figure(figsize=figsize, facecolor=_BACKGROUND)
    ax = fig.subplots()
    ax.set_facecolor(_BACKGROUND)

    for i, mark in enumerate(marks):
        accent = is_accent(mark)
        ax.text(
            coords[i, 0],
            coords[i, 1],
            mark,
            fontsize=sizes[i] * (_ACCENT_SCALE if accent else 1.0),
            color=_ACCENT if accent else _DIM,
            fontweight="bold" if accent else "normal",
            rotation=angles[i],
            ha="center",
            va="center",
            zorder=3 if accent else 2,
        )

    frame_axes(ax, coords, margin=0.08)
    fig.tight_layout()
    return fig

lexograph.presets.punctuation_spiral.is_accent(char)

Return whether char is a logical, mathematical, or Greek sign.

Parameters:

Name Type Description Default
char str

A single character.

required

Returns:

Type Description
bool

True if the character should take the gold accent colour.

Examples:

>>> is_accent("=")
True
>>> is_accent(",")
False
Source code in lexograph/presets/punctuation_spiral.py
def is_accent(char: str) -> bool:
    """Return whether ``char`` is a logical, mathematical, or Greek sign.

    Args:
        char: A single character.

    Returns:
        ``True`` if the character should take the gold accent colour.

    Examples:
        >>> is_accent("=")
        True
        >>> is_accent(",")
        False
    """
    if char in _ACCENT_CHARS:
        return True
    codepoint = ord(char)
    return any(lo <= codepoint <= hi for lo, hi in _ACCENT_RANGES)