API reference

The package exposes a function and an artist class. Use the curved_text function for the common case. Use the CurvedText class when you need to keep a reference to the label and change it after drawing.

curved_text.curved_text(ax: Axes, x: ArrayLike, y: ArrayLike, text: str, *, pos: float = 0.5, anchor: str = 'center', offset: float = 0.0, box: bool | str | dict = False, crowding: str = 'none', valign: str = 'center', **kwargs: Any) CurvedText[source]

Draw text along the curve (x, y) on ax and return the artist.

Thin convenience wrapper around CurvedText; see it for the meaning of pos, anchor, offset, box, crowding, and valign. The axes is the first argument here, matching matplotlib’s axes-first helper functions, whereas CurvedText takes it after x, y, text to match matplotlib.text.Text.

class curved_text.CurvedText(x: ArrayLike, y: ArrayLike, text: str, axes: Axes, *, pos: float = 0.5, anchor: str = 'center', offset: float = 0.0, box: bool | str | dict = False, crowding: str = 'none', valign: str = 'center', **kwargs: Any)[source]

Bases: Text

A string drawn along an (x, y) curve, one segment at a time (each plain character and each $...$ run).

Every glyph – plain character or mathtext run – is laid out on one shared text baseline and mapped onto the curve from its glyph outline: a plain character rigidly (one rotation by the chord across its own advance, so its shape is undistorted), a mathtext run by bending its outlines so radicals and fractions stay connected. valign chooses which line of the text rides the curve – the vertical centre by default. Because both kinds share the baseline, plain and math sit level by construction, and because each glyph is placed by an isometry there is no per-glyph drift. The rotation chord follows the local tangent but averages over the glyph’s own width, so rotation stays smooth across the vertices of a coarsely sampled polyline instead of snapping to each segment’s angle. The layout is recomputed on every draw, so the label keeps following the curve through figure layout, resizing, and interactive panning or zooming.

Placement controls:

pos

Where the label is anchored along the curve, as a fraction of the curve’s arc length: 0.0 is the first point, 1.0 is the last.

anchor

Which part of the label lands at pos: "start", "center", or "end".

offset

A perpendicular shift off the curve, in typographic points. The label is laid along the parallel (offset) curve at that distance, so the clearance from the curve is uniform along the whole label and the letter spacing stays even, even where the curvature is steep or asymmetric. Positive is to the left of the direction of travel, which is visually above a left-to-right curve. pos and anchor stay measured against the original curve and are carried perpendicularly onto the offset curve, so an offset label sits directly off the spot the same pos marks on the bare curve.

valign

Which line of the text rides the curve: "center" (the default – the text straddles the curve), "baseline" (the baseline follows the curve, so the body sits above it with descenders below), "ascender", or "descender". The choice is a single font-metric shift applied identically to every glyph and to the mathtext runs, so it never introduces a per-glyph step and keeps plain and math aligned. Combine with offset to lift the chosen line off the curve.

A label that overruns either end of the curve – because of pos and anchor – is not clipped. The curve is extended along its end tangent and the overrunning glyphs are placed on that straight extension.

Set box to draw a casing behind the label – a band that follows the curve at the label’s height, drawn under the glyphs – so the label stays legible where it crosses the lines it labels. For a lighter, glyph-hugging casing instead, pass a white withStroke through path_effects; a wide stroke there merges adjacent per-character glyphs, so box is the way to get solid coverage under plain text.

Mathtext is supported: each $...$ run in text is laid out by matplotlib’s mathtext engine and bent continuously along the curve – every glyph outline and rule box is mapped through the curve’s arc-length frame, so radicals, fractions, and sized delimiters stay connected at any curvature. The run rides the same baseline as the surrounding plain glyphs, so its main symbols sit level with them. Pass parse_math=False to treat dollar signs literally. text.usetex is not supported. Tall expressions compress vertically on the inside of tight bends, so choose label size relative to curvature accordingly.

Both plain glyphs and mathtext runs are rendered from their glyph outlines rather than as hinted Text artists. On a rotated label this is what lets a single baseline be pinned exactly; the only cost is the loss of pixel-grid hinting, which is marginal on rotated text and matches how mathtext has always rendered.

Parameters:
  • x (array-like) – The curve in data coordinates: 1-D, equal length, at least two points, finite, and ordered along the curve.

  • y (array-like) – The curve in data coordinates: 1-D, equal length, at least two points, finite, and ordered along the curve.

  • text (str) – The string to draw. May contain mathtext runs ($...$).

  • axes (matplotlib.axes.Axes) – The axes to draw into.

  • pos (float, default 0.5) – Arc-length fraction in [0, 1] for the anchor point.

  • anchor ({"start", "center", "end"}, default "center") – Which part of the label sits at pos.

  • offset (float, default 0.0) – Perpendicular offset off the curve, in points. The label is laid along the parallel (offset) curve at that distance.

  • box (bool, str, or dict, default False) – A casing drawn behind the label to clear the lines it crosses. True draws a white band; a color string sets its color; a dict accepts color, pad (band height relative to the tallest glyph, default 1.1), and alpha. The band has rounded ends, so it extends about half its height past the first and last glyph.

  • crowding ({"none", "curvature"}, default "none") – How to space glyphs around bends. "none" advances each glyph by its own width, so on the concave side of a tight bend the rotated glyph boxes can overlap. "curvature" opens an even letterspacing gap that grows with the local curvature and the glyph height, so the inside edges stop colliding; the gap is the same between every pair of letters, and a deadband leaves gentle bends and straight runs unchanged.

  • valign ({"center", "baseline", "ascender", "descender"}, default "center") – Which line of the text rides the curve. "center" straddles the text on the curve (the default); "baseline" follows the text baseline so the body sits above the curve; the others ride the ascender or descender line. Each is a constant font-metric shift applied to the whole label.

  • **kwargs – Passed to each per-character glyph and each mathtext run (for example color, fontsize, alpha, fontfamily).

set_zorder(zorder) None[source]

Set the zorder for the artist. Artists with lower zorder values are drawn first.

Parameters:

level (float)

remove() None[source]

Remove the artist from the figure if possible.

The effect will not be visible until the figure is redrawn, e.g., with .FigureCanvasBase.draw_idle. Call ~.axes.Axes.relim to update the Axes limits if desired.

Note: there is no support for removing the artist’s legend entry.

draw(renderer, *args, **kwargs) None[source]

Draw the Artist (and its children) using the given renderer.

This has no effect if the artist is not visible (.Artist.get_visible returns False).

Parameters:

renderer (~matplotlib.backend_bases.RendererBase subclass.)

Notes

This method is overridden in the Artist subclasses.