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.normpathfrom a list elems of alternating knots and links.If elems begins with a
beginknotand ends with anendknot, an open path is constructed.If elems contains only interior knots (
smoothknotorroughknot) 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
pathby placing anendknotfollowed immediately by a newbeginknot(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 viaset()).
- 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 forbeginknot, or radians forbeginknot_pt).startknotis an alias forbeginknot.
- 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 forendknot, or radians forendknot_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.
knotis an alias forsmoothknot.
- 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, whereasroughknot(x, y, langle=30, rangle=90)produces a sharp corner).
Links¶
Links are placed between consecutive knots in the list passed to path
and determine how the two knots are connected.
- class metapost.path.tensioncurve(ltension=1, latleast=False, rtension=None, ratleast=None)¶
- class metapost.path.curve(ltension=1, latleast=False, rtension=None, ratleast=None)¶
Connects two knots with a cubic Bézier spline segment determined by the MetaPost solver.
ltension: Tension at the starting (left) knot of the segment (default
1; must be at least0.75). Higher tension pulls the curve tighter toward a straight line between the knots.latleast: When
True, corresponds to MetaPost’stension atleastdirective at the starting knot, which increases the tension automatically if needed to avoid inflection points near the knot.rtension: Tension at the ending (right) knot of the segment (defaults to ltension when
None).ratleast: When
True, appliestension atleastat the ending knot (defaults to latleast whenNone).
curveis an alias fortensioncurve.
- class metapost.path.controlcurve(lcontrol, rcontrol)¶
- class metapost.path.controlcurve_pt(lcontrol_pt, rcontrol_pt)¶
Connects two knots with a cubic Bézier curve whose two control points are set explicitly to the coordinate 2-tuples lcontrol and rcontrol (in user coordinates or points). Adjacent smooth curves on either side of those knots will adapt their tangents and curvatures to match this explicit segment.
- class metapost.path.line(keepangles=False)¶
Connects two knots with a straight line segment.
When keepangles is
False(the default), the straight line acts as an endpoint boundary for adjacent curves (equivalent to--in MetaPost), allowing corners at the knots.When keepangles is
True, the tangent directions of the curves entering the left knot and leaving the right knot are constrained to match the angle of the straight line segment (equivalent to---in MetaPost), ensuring a smooth tangent transition into and out of the straight segment.