Paths¶
Paths are the geometric backbone of PyX. A path describes a sequence of connected or disconnected line segments, circular or elliptical arcs, and cubic Bézier curves in the plane.
PyX provides two complementary representations of paths:
path.path: A PostScript-style path composed of arbitrarypathiteminstances (moveto,lineto,curveto,arc,closepath, etc.). When written to PostScript or SVG, these path items map directly to native PostScript operators or SVG path commands.normpath.normpath: A normalized path consisting of a list ofnormpath.normsubpathobjects, which in turn contain only straight lines (normpath.normline_pt) and cubic Bézier curves (normpath.normcurve_pt) in absolute point coordinates, subject to a numerical accuracy epsilon (default \(10^{-5}\,\text{pt}\)). All advanced geometric operations—such as computing arc lengths, finding intersections, splitting paths, or evaluating tangents and curvatures—are performed on normalized paths.
Because path.path automatically delegates geometric queries to its
cached path.normpath() representation, you can call methods like
path.intersect(), path.at(), path.split(), or
path.tangent() directly on any path.path instance.
Constructing paths¶
A path can be constructed either by passing pathitem instances
to path or by using one of the predefined shape classes such as
line, rect, circle, ellipse, or
curve.
For example, to construct a triangle with a rounded top corner and intersect it with a circle:
from pyx import *
c = canvas.canvas()
triangle = path.path(path.moveto(0, 0),
path.lineto(4, 0),
path.arct(2, 3, 0, 0, 0.5),
path.closepath())
circle = path.circle(2, 1, 1.5)
c.stroke(triangle)
c.stroke(circle, [color.rgb.blue])
# Mark all intersection points with small red circles
for p in triangle.intersect(circle)[0]:
x, y = triangle.at(p)
c.fill(path.circle(x, y, 0.08), [color.rgb.red])
c.writePDFfile("intersect")
Two paths can be combined in two different ways:
Addition (
p = p1 + p2orp1 += p2): Concatenates the subpaths ofp1andp2into a single compound path without connecting the end ofp1to the start ofp2.Joining (
p = p1 << p2orp = p1.joined(p2)orp1.join(p2)): Connects the end of the last open subpath ofp1to the beginning of the first open subpath ofp2(inserting a straight line segment if the two endpoints do not coincide) so that they form a single continuous subpath.
Path items¶
Every element passed to path must be an instance of a subclass of
pathitem. Each coordinate-accepting class has both a standard version
(taking coordinates in PyX user lengths or explicit unit lengths) and a
_pt version (taking coordinates in PostScript points).
Subpath control¶
- class path.moveto(x, y)¶
- class path.moveto_pt(x_pt, y_pt)¶
Starts a new subpath and sets the current point to \((x, y)\).
- class path.rmoveto(dx, dy)¶
- class path.rmoveto_pt(dx_pt, dy_pt)¶
Starts a new subpath and moves the current point by \((\Delta x, \Delta y)\) relative to the previous current point.
- class path.closepath¶
Closes the current subpath by connecting the current point back to the starting point of the subpath with a straight line segment.
Straight lines¶
- class path.lineto(x, y)¶
- class path.lineto_pt(x_pt, y_pt)¶
Appends a straight line segment from the current point to \((x, y)\), which becomes the new current point.
- class path.rlineto(dx, dy)¶
- class path.rlineto_pt(dx_pt, dy_pt)¶
Appends a straight line segment from the current point \((x_0, y_0)\) to \((x_0 + \Delta x, y_0 + \Delta y)\), which becomes the new current point.
- class path.multilineto_pt(points_pt)¶
Appends straight line segments from the current point through a sequence of coordinate pairs
[(x1_pt, y1_pt), (x2_pt, y2_pt), ...]in points. This class is provided for performance when constructing polylines with many vertices.
Circular arcs¶
- class path.arc(x, y, r, angle1, angle2)¶
- class path.arc_pt(x_pt, y_pt, r_pt, angle1, angle2)¶
Appends a counterclockwise circular arc centered at \((x, y)\) with radius r from angle1 to angle2 (in degrees). If the path already has a current point in an open subpath, a straight line segment is first added from the current point to the starting point of the arc; otherwise, the arc starts a new subpath. After the arc is added, the current point is the endpoint of the arc at angle2.
- class path.arcn(x, y, r, angle1, angle2)¶
- class path.arcn_pt(x_pt, y_pt, r_pt, angle1, angle2)¶
Same as
arc, except that the arc is drawn in the clockwise direction from angle1 down to angle2.
- class path.arct(x1, y1, x2, y2, r)¶
- class path.arct_pt(x1_pt, y1_pt, x2_pt, y2_pt, r_pt)¶
Appends a circular arc of radius r that is tangent both to the line from the current point \((x_0, y_0)\) to \((x_1, y_1)\) and to the line from \((x_1, y_1)\) to \((x_2, y_2)\). A straight line segment is first drawn from \((x_0, y_0)\) to the first point of tangency \((x_{t,1}, y_{t,1})\), followed by the tangent arc ending at the second point of tangency \((x_{t,2}, y_{t,2})\), which becomes the new current point:
Bézier curves¶
- class path.curveto(x1, y1, x2, y2, x3, y3)¶
- class path.curveto_pt(x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt)¶
Appends a cubic Bézier curve from the current point \((x_0, y_0)\) to \((x_3, y_3)\) with first control point \((x_1, y_1)\) and second control point \((x_2, y_2)\). The parametric curve is given by
\[\mathbf{B}(t) = (1-t)^3 \mathbf{P}_0 + 3(1-t)^2 t \mathbf{P}_1 + 3(1-t) t^2 \mathbf{P}_2 + t^3 \mathbf{P}_3, \quad t \in [0, 1].\]After the curve is added, \((x_3, y_3)\) becomes the new current point.
- class path.rcurveto(dx1, dy1, dx2, dy2, dx3, dy3)¶
- class path.rcurveto_pt(dx1_pt, dy1_pt, dx2_pt, dy2_pt, dx3_pt, dy3_pt)¶
Appends a cubic Bézier curve whose control points and endpoint are specified relative to the current point \((x_0, y_0)\) before the operation: \((x_0 + \Delta x_1, y_0 + \Delta y_1)\), \((x_0 + \Delta x_2, y_0 + \Delta y_2)\), and \((x_0 + \Delta x_3, y_0 + \Delta y_3)\).
- class path.multicurveto_pt(points_pt)¶
Appends multiple cubic Bézier curves from a sequence of 6-tuples
[(x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt), ...]in points, provided for performance when constructing paths with many curve segments.
Class path¶
- class path.path(*pathitems)¶
Constructs a path from the given pathitems.
A
pathinstance supports sequence indexing (p[i]returns the i-thpathitem),len(p)(number of path items),p1 + p2andp1 += p2(concatenating subpaths), andp1 << p2(joining paths viajoined()).Note
In the methods below, any parameter params that specifies a location (or a list/tuple of locations) along the path accepts either:
a
normpath.normpathparaminstance (as returned bybegin(),end(),arclentoparam(), orintersect()), ora PyX length (or number in user units, or in points for
_ptmethods), which is automatically interpreted as the arc length measured from the beginning of the path!
Furthermore, methods decorated to accept params or lengths (such as
at(),arclentoparam(),paramtoarclen(),curvature_pt(),rotation(),tangent(), andtrafo()) return a single value when passed a single parameter, and a list of values when passed a sequence of parameters.- append(apathitem)¶
Appends a single
pathitemto the path in place.
- extend(pathitems)¶
Appends an iterable of
pathiteminstances to the path in place.
- arclentoparam(lengths)¶
- arclentoparam_pt(lengths_pt)¶
Returns the
normpath.normpathparaminstance(s) corresponding to the given arc length(s).
- paramtoarclen(params)¶
- paramtoarclen_pt(params)¶
Returns the arc length(s) (as PyX lengths or in points) corresponding to the given
normpath.normpathparaminstance(s).
- at(params)¶
- at_pt(params)¶
Returns the coordinate tuple
(x, y)of PyX lengths (or(x_pt, y_pt)in points) at the specified parameter(s) or arc length(s).
- atbegin()¶
- atbegin_pt()¶
Returns the coordinate tuple
(x, y)(or(x_pt, y_pt)) of the start point of the first subpath.
- atend()¶
- atend_pt()¶
Returns the coordinate tuple
(x, y)(or(x_pt, y_pt)) of the end point of the last subpath.
- begin()¶
Returns the
normpath.normpathparamcorresponding to the beginning of the path.
- end()¶
Returns the
normpath.normpathparamcorresponding to the end of the path.
- curvature_pt(params)¶
Returns the signed curvature in \(\text{pt}^{-1}\) at the specified parameter(s) or arc length(s) in points. The curvature is positive when the path turns to the left (counterclockwise) and negative when it turns to the right.
- intersect(other)¶
Intersects the path with other (another
pathornormpath.normpath) and returns a 2-tuple(params_self, params_other)of lists ofnormpath.normpathparaminstances for the intersection points alongselfandother, sorted alongself.
- join(other)¶
Joins other to the end of
selfin place and returnsself.
- joined(other)¶
Returns a new path consisting of
selfand other joined together (also available via the<<operator:self << other).
- normpath(epsilon=...)¶
Converts the path into a
normpath.normpathinstance. When epsilon is omitted, the default normalized path is cached on the path instance.
- path()¶
Returns
self(provided so thatpathandnormpath.normpathcan be used interchangeably).
- reversed()¶
Returns a new
normpath.normpathwith the orientation of all subpaths reversed.
- rotation(params)¶
- rotation_pt(params)¶
Returns a
trafo.traforotation at params (or arc lengths) that rotates the positive x-axis onto the unit tangent vector of the path and the positive y-axis onto the left-hand normal vector.
- split(params)¶
- split_pt(params)¶
Splits the normalized path at the given parameter(s) or arc length(s) and returns a list of
normpath.normpathsegments. Note thateven when a single split point is passed, a list of two path segments is returned.
- tangent(params, length=1)¶
- tangent_pt(params, length_pt)¶
Returns a
normpath.normpathline segment of length length (default 1 user unit) starting at the point params and pointing in the tangent direction of the path.
- trafo(params)¶
- trafo_pt(params)¶
Returns a
trafo.trafotransformation at params (or arc lengths) that maps the origin \((0, 0)\) to the point on the path and rotates the coordinate axes so that the local x-axis points along the path tangent and the local y-axis points along the left-hand normal.
- transformed(trafo)¶
Returns a new
normpath.normpathtransformed according to the affine transformation trafo.
Normalized paths: normpath¶
While casual users rarely need to instantiate classes from normpath
directly, PyX developers and advanced users working with intersections, path
splitting, or custom decorators and deformers interact closely with
normpath, normsubpath, and normpathparam.
Why normalized paths?¶
PostScript paths can contain arcs, relative displacements, and unclosed or
implicitly connected subpaths. Evaluating intersections or arc-length integrals
directly on such heterogeneous items would be error-prone and slow. PyX
therefore normalizes any path.path into a normpath:
A
normpathis a list ofnormsubpathinstances.Each
normsubpathis a contiguous open or closed chain ofnormsubpathiteminstances—exclusivelynormline_pt(straight lines) andnormcurve_pt(cubic Bézier curves)—together with a precision length epsilon (default \(10^{-5}\,\text{pt}\)).Line or curve segments shorter than epsilon are accumulated as a
skippedlinerather than creating degenerate zero-length items with undefined tangents, and are merged into the adjacent segment once the accumulated displacement exceeds epsilon.
- normpath.set(epsilon=None)¶
Sets the global default accuracy epsilon in points (initially
1e-5) used when constructing newnormsubpathinstances.
Class normpathparam¶
- class normpath.normpathparam(normpath, normsubpathindex, normsubpathparam)¶
Identifies a point along normpath. Here normsubpathindex is the integer index of the
normsubpathwithin normpath, and normsubpathparam is a floating-point parameter \(t\) along that subpath, where the integer part \(\lfloor t \rfloor\) selects thenormsubpathitemand the fractional part \(t - \lfloor t \rfloor \in [0, 1]\) is the parameter on that line or Bézier curve.normpathparaminstances support rich arithmetic and comparisons via arc length:Adding or subtracting a PyX length (e.g.
p + 0.5*unit.t_cmorp - 1) moves along the path by that arc length and returns the resultingnormpathparam!Adding or subtracting two
normpathparaminstances belonging to the samenormpath, or multiplying/dividing anormpathparamby a scalar factor, operates on their arc lengths and returns the correspondingnormpathparam.Comparing two
normpathparaminstances (p1 < p2,p1 == p2) compares their positions(normsubpathindex, normsubpathparam)along the path; comparing anormpathparamwith a length compares its arc length from the start of the path with that length.
Class normpath¶
- class normpath.normpath(normsubpaths=None)¶
Constructs a normalized path from a list of
normsubpathinstances (or an empty list ifNone).normpathprovides all the geometric and manipulation methods ofpath.path(path.path.append(),path.path.extend(),path.path.arclen(),path.path.arclen_pt(),path.path.arclentoparam(),path.path.arclentoparam_pt(),path.path.at(),path.path.at_pt(),path.path.atbegin(),path.path.atbegin_pt(),path.path.atend(),path.path.atend_pt(),path.path.bbox(),path.path.begin(),path.path.end(),path.path.curvature_pt(),path.path.intersect(),path.path.join(),path.path.joined(),path.path.normpath(),path.path.paramtoarclen(),path.path.paramtoarclen_pt(),path.path.reversed(),path.path.rotation(),path.path.rotation_pt(),path.path.split(),path.path.split_pt(),path.path.tangent(),path.path.tangent_pt(),path.path.trafo(),path.path.trafo_pt(), andpath.path.transformed()), plus:- path()¶
Converts the
normpathback into an equivalentpath.pathconsisting ofpath.moveto_pt,path.lineto_pt,path.curveto_pt, andpath.closepathitems.
Indexing
np[i]returns the i-thnormsubpath, andlen(np)returns the number of subpaths.
Class normsubpath and subpath items¶
- class normpath.normsubpath(normsubpathitems=[], closed=0, epsilon=_marker)¶
Represents a single connected open or closed normalized subpath.
normsubpathitems: List of
normline_ptandnormcurve_ptinstances. Adjacent items must share endpoints within epsilon.closed: Boolean or integer flag indicating whether the subpath is closed.
epsilon: Precision threshold in points (defaults to the module-level
normpath._epsilon = 1e-5, orNoneto disable filtering of short segments).
Key attributes and methods:
- normsubpathitems¶
The list of
normline_ptandnormcurve_ptitems in the subpath.
- closed¶
Whether the subpath is closed.
- epsilon¶
Accuracy threshold in points for this subpath.
- append(anormsubpathitem)¶
- extend(normsubpathitems)¶
Appends one or more
normsubpathiteminstances to an open subpath, automatically coalescing segments shorter thanepsilon.
- close()¶
Closes the subpath in place, appending a closing
normline_ptback to the start point if necessary.
- segments(params)¶
Takes a sequence of at least two subpath parameter floats and returns a list of
normsubpathinstances for the segments between consecutive parameters.
- pathitems()¶
Returns the list of
path.pathiteminstances representing this subpath.
- class normpath.normline_pt(x0_pt, y0_pt, x1_pt, y1_pt)¶
A normalized straight line segment from
(x0_pt, y0_pt)to(x1_pt, y1_pt)in points.
- class normpath.normcurve_pt(x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt)¶
A normalized cubic Bézier curve segment with control points
(x0_pt, y0_pt)through(x3_pt, y3_pt)in points.
Predefined shapes¶
For convenience, path provides subclasses of path for common
geometric primitives, each in a user-length version and a _pt version:
- class path.line(x1, y1, x2, y2)¶
- class path.line_pt(x1_pt, y1_pt, x2_pt, y2_pt)¶
A straight line segment from \((x_1, y_1)\) to \((x_2, y_2)\).
- class path.curve(x0, y0, x1, y1, x2, y2, x3, y3)¶
- class path.curve_pt(x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt)¶
A single cubic Bézier curve from \((x_0, y_0)\) to \((x_3, y_3)\) with control points \((x_1, y_1)\) and \((x_2, y_2)\).
- class path.rect(x, y, width, height)¶
- class path.rect_pt(x_pt, y_pt, width_pt, height_pt)¶
A closed axis-aligned rectangle with lower-left corner \((x, y)\), width, and height.