MetaPost-like paths

In addition to constructing paths with explicit Bézier control points via path, PyX provides the metapost.path module, which implements the spline-choosing algorithm of John Hobby and Donald Knuth from MetaPost. Instead of specifying control points manually, you specify a sequence of knots (points through which the curve must pass, optionally with tangent angles or endpoint curvatures) separated by links (such as smooth curves with adjustable tension or straight lines). PyX then solves the global system of equations to determine cubic Bézier control points that maintain continuous tangents and “mock” curvatures at all interior smooth knots.

The resulting metapost.path.path is a subclass of normpath.normpath, so it supports all standard PyX path and normalized path operations (such as stroking, filling, intersecting, splitting, and deforming).

Example

To draw a closed smooth curve passing through four points, and an open curve with prescribed start and end tangent angles:

from pyx import *
from pyx.metapost.path import beginknot, endknot, smoothknot, roughknot, curve, line

c = canvas.canvas()

# Closed smooth loop through three knots
closed_loop = metapost.path.path([
    smoothknot(0, 0), curve(),
    smoothknot(2, 1), curve(),
    smoothknot(1, 2.5), curve()
])
c.stroke(closed_loop, [color.rgb.blue])

# Open curve starting at 45 degrees and ending with a straight line segment
open_curve = metapost.path.path([
    beginknot(3, 0, angle=45), curve(ltension=1.2),
    smoothknot(4.5, 1.5), line(keepangles=True),
    endknot(6, 0.5)
])
c.stroke(open_curve, [color.rgb.red])

c.writePDFfile("metapost")

Path construction: path

class metapost.path.path(elems, epsilon=None)

Constructs a normpath.normpath from a list elems of alternating knots and links.

  • If elems begins with a beginknot and ends with an endknot, an open path is constructed.

  • If elems contains only interior knots (smoothknot or roughknot) and links, the last link connects the last knot back to the first knot, forming a closed path.

  • Multiple open subpaths can be constructed within a single path by placing an endknot followed immediately by a new beginknot (without a link between them).

  • epsilon specifies the numerical precision in points used for the resulting normpath.normsubpath (defaulting to the module-level precision set via set()).

metapost.path.set(epsilon=None)

Sets the module-level default precision epsilon in points (initially 1e-5).

Knots

Knots define the points through which the path passes and specify boundary or continuity conditions for the incoming (left) and outgoing (right) curve segments. Every knot class is available both in a user-length version (where angles are given in degrees) and a _pt version (where coordinates are given in points and angles in radians).

class metapost.path.beginknot(x, y, curl=1, angle=None)
class metapost.path.beginknot_pt(x_pt, y_pt, curl=1, angle=None)
class metapost.path.startknot(x, y, curl=1, angle=None)

Starts an open subpath at \((x, y)\). You may specify either the relative endpoint curvature curl (default 1, meaning the curvature at the endpoint matches that of the adjacent knot) or an explicit outgoing tangent direction angle (in degrees for beginknot, or radians for beginknot_pt). startknot is an alias for beginknot.

class metapost.path.endknot(x, y, curl=1, angle=None)
class metapost.path.endknot_pt(x_pt, y_pt, curl=1, angle=None)

Ends an open subpath at \((x, y)\). You may specify either the relative endpoint curvature curl (default 1) or an explicit incoming tangent direction angle (in degrees for endknot, or radians for endknot_pt).

class metapost.path.smoothknot(x, y)
class metapost.path.smoothknot_pt(x_pt, y_pt)
class metapost.path.knot(x, y)

An interior knot at \((x, y)\) where both the tangent direction and the mock curvature are continuous across the knot. knot is an alias for smoothknot.

class metapost.path.roughknot(x, y, lcurl=1, rcurl=None, langle=None, rangle=None)
class metapost.path.roughknot_pt(x_pt, y_pt, lcurl=1, rcurl=None, langle=None, rangle=None)

An interior knot at \((x, y)\) where the curve may have a corner or prescribed left/right curls or tangent angles.

  • On the incoming (left) side, specify either lcurl (default 1) or langle (incoming tangent angle).

  • On the outgoing (right) side, specify either rcurl or rangle (outgoing tangent angle). If neither rcurl nor rangle is given, the right-side condition defaults to the same type and value as the left-side condition (so roughknot(x, y, langle=30) produces a smooth tangent at 30 degrees, whereas roughknot(x, y, langle=30, rangle=90) produces a sharp corner).