Sampling and export
The exporters write geometry only. They choose no colour, line width, theme,
axis, label or canvas, and they never flatten a cubic into line pieces: TikZ
gets native .. controls .. curves and SVG native C commands.
Sampling
UniformSampler(int).sample(curve)
Evaluate any curve or path at count equally spaced parameters over its
domain, both ends included (count \(\ge 2\)), in bezierkit.sampling.
Sample
The result of sampling: the read-only parameters t and the points, with
the coordinate arrays x and y and iteration over (t, point) pairs.
from bezierkit.sampling import UniformSampler
sample = UniformSampler(5).sample(curve)
# [0. 0.90625 2. 3.09375 4. ]
print(sample.x)
Rounding and transforms
The text exporters write each coordinate with a fixed number of decimal
places precision, and accept a transform that maps each control point
(a Point) to a two-dimensional Point before it is written, for example
from data to page coordinates.
Proposition · Rounding error
Let \(B\) and \(\widetilde{B}\) be Bézier curves of the same degree \(n\) in \(\mathbb{R}^d\) with control points \(P_i\) and \(\widetilde{P}_i\), and let \(|\widetilde{P}_{i,k} - P_{i,k}| \le \epsilon\) for every \(i\) and every coordinate \(k\). Then for every \(t \in [0, 1]\) and every \(k\)
Rounding every coordinate to \(p\) decimal places gives \(\epsilon = 1/2 \cdot 10^{-p}\).
So with the default precision=6, an exported planar curve stays within
\(0.71 \times 10^{-6}\) of the original everywhere, not only at its control
points. An affine transform is exact by Affine invariance; a non-affine one
moves the control points correctly but not, in general, the points between
them.
JSON
dumps(
path,
*,
metadata=None,
indent=None
)
In bezierkit.export.json. Writes a path in the versioned schema of
JSON path schema, version 1, with sorted keys and no NaN or infinity.
loads(str)
Validates a document and returns a PathDocument holding the path and
its metadata. An unknown schema or version, a segment without exactly
four points, a point of the wrong dimension, or a non-finite number raises
ValueError.
PathDocument
A path together with its caller metadata, as returned by loads().
The string "bezierkit.path"
The integer 1; readers reject other versions
The dimension \(d\) of every point
A non-empty array of {"closed": bool, "segments": [...]}, each segment four points of \(d\) numbers
A JSON object, passed through uninterpreted
from bezierkit.export.json import dumps, loads
text = dumps(path, metadata={"name": "arch"})
# {"dimension":2,"metadata":{"name":"arch"},"schema":"bezierkit.path",
# "subpaths":[{"closed":false,"segments":[[[0.0,0.0],[1.0,2.0],...]]}],"version":1}
assert loads(text).path.segments == path.segments
Version 1 is frozen: a new required key, a change in closure semantics or in the control-point layout requires version 2.
SVG
to_svg_path_data(
path,
*,
precision=6,
transform=None
)
from_svg_path_data(str)
In bezierkit.export.svg. The path data string (the d attribute) of a
planar path, using only absolute M, C and Z commands, one M per
subpath; no SVG document or styling. from_svg_path_data() parses that
same subset back; relative commands, lines and arcs raise ValueError.
from bezierkit.export.svg import to_svg_path_data
# M 0.00 0.00 C 1.00 2.00 3.00 2.00 4.00 0.00
print(to_svg_path_data(path, precision=2))
TikZ
to_tikz(
path,
*,
precision=6,
transform=None,
options=None
)
segment_to_tikz(
segment,
*,
precision=6,
transform=None,
include_start=True
)
In bezierkit.export.tikz. One \draw command per subpath, each segment
written as .. controls (P1) and (P2) .. (P3), closed subpaths ending in
-- cycle. options is inserted verbatim as \draw[options]; nothing is
chosen by default. segment_to_tikz() writes one segment, without the
\draw, for callers assembling their own commands.
from bezierkit.export.tikz import to_tikz
print(to_tikz(path, precision=2, options="thick"))
# \draw[thick] (0.00,0.00) .. controls (1.00,2.00) and (3.00,2.00) .. (4.00,0.00);
Every figure in this manual was made this way: a script builds the curves
with bezierkit, writes them with to_tikz() into a standalone
LaTeX document beside the axes and labels, and compiles it to PDF. The
.tex source of each figure ships with the manual.
Matplotlib
from_path(
path,
*,
transform=None
)
to_path(path)
approximate_path(
path,
transform,
*,
tolerance,
max_depth=20
)
In bezierkit.adapters.matplotlib, with the matplotlib extra.
from_path() converts a Matplotlib Path exactly: MOVETO, LINETO,
CURVE3, CURVE4 and CLOSEPOLY become cubics, lines and quadratics by
Lines and quadratics as cubics. An affine transform is applied to the control points,
which is exact (Affine invariance); a non-affine one raises ValueError.
to_path() converts back, one CURVE4 per segment.
A non-affine transform, such as a logarithmic axis, does not map cubics to
cubics. approximate_path() is the explicit opt-in: it subdivides each
transformed segment until its midpoints lie within tolerance / 2 of the
chords, then simplifies the points with fit_polyline at tolerance / 2.
It does not claim to be exact.