Skip to main content

bezierkit

bezierkit

PyPI Python License Tests

A small mathematical toolkit for constructing and analyzing Bézier curves.

bezierkit is renderer-independent: it provides curve construction, evaluation, subdivision, and sampling while leaving plotting to consumers such as EconViz, Matplotlib, Plotly, SVG, or Typst adapters.

Installation

Add the library to your project with uv:

uv add bezierkit

Add the optional command-line interface:

uv add "bezierkit[cli]"

For development, clone the repo and sync all extras:

uv sync --all-extras --dev

Run anything inside the environment with uv run, e.g. uv run pytest or uv run bezierkit --help. A plain pip install bezierkit / pip install "bezierkit[cli]" also works if you are not using uv.

Python API

from bezierkit import BezierCurve, CubicBezierSegment, PiecewiseBezier, Point
from bezierkit.construction import PlanarSlopes
from bezierkit.sampling import UniformSampler

curve = BezierCurve.cubic(
    Point(0, 0),
    Point(1, 2),
    Point(3, 2),
    Point(4, 0),
)

segment = CubicBezierSegment(
    Point(0, 0),
    Point(1, 2),
    Point(3, 2),
    Point(4, 0),
)
segment.control_points        # (P0, P1, P2, P3)

path = PiecewiseBezier([
    CubicBezierSegment.from_line(Point(0, 0), Point(2, 0)),
    CubicBezierSegment.from_line(Point(2, 0), Point(2, 4)),
])
path.at(0.75)                 # Point(2.0, 2.0)

curve.at(0.5)                 # Point(2.0, 1.5)
curve.derivative().at(0.5)    # first derivative
left, right = curve.split(0.3)
sample = UniformSampler(200).sample(curve)

demand = PlanarSlopes(
    start=Point(0, 5),
    end=Point(5, 0),
    start_slope=-2,
    end_slope=-0.3,
).build()

The engine supports arbitrary degree and dimension. Point, Vector, PointSet, control polygons, and sampling results are immutable value objects.

CubicBezierSegment is the primary renderer-facing value object. It exposes p0, p1, p2, p3, tight axis-aligned bounds, and cubic-preserving split and reversal operations. Existing BezierCurve.cubic(...) code remains valid; use CubicBezierSegment.from_curve(curve) to migrate without changing any control point, or segment.as_curve() when a degree-generic API is required.

PiecewiseBezier divides [0, 1] uniformly across the segments of a single subpath. Closure is metadata and never inserts a hidden closing segment. PiecewiseBezier.compound(...) preserves independent subpaths for fills and holes; evaluate and split each subpath independently because a compound path has no single continuous parameterization. Reversal preserves each subpath; splitting is defined only for a single open subpath.

Linear and quadratic inputs can be elevated without changing their geometry:

from bezierkit.bezier import to_cubic

cubic = to_cubic(BezierCurve.quadratic(
    Point(0, 0), Point(3, 6), Point(9, 0)
))

Batch evaluation uses a vectorized de Casteljau implementation by default. BernsteinEvaluator is also available from bezierkit.bezier.evaluation for explicit low-degree throughput tradeoffs; see benchmarks/ for the reproducible 400, 10,000, and 100,000-value benchmark.

Interpolation, fitting, and level sets

from bezierkit.fitting import fit_graph
from bezierkit.implicit import trace_implicit
from bezierkit.interpolation import graph_hermite

segment = graph_hermite(
    x0=1, x1=1.5,
    y0=4, y1=8 / 3,
    m0=-8, m1=-32 / 9,
)

path = fit_graph(
    lambda x: 4 / x**2,
    lambda x: -8 / x**3,
    x0=0.8, x1=3,
    tolerance=1e-3,
)

contours = trace_implicit(
    lambda x, y: x**2 * y,
    levels=[1, 2, 4],
    viewport=(0.5, 4, 0, 6),
    resolution=(121, 121),
    tolerance=0.01,
    gradient=lambda x, y: (2 * x * y, x**2),
)

Adaptive fitting reports a measured Euclidean error on every segment and raises ToleranceNotMet if configured limits prevent the requested tolerance. Implicit tracing preserves disconnected and closed components as independent paths and uses parametric gradient tangents, including at vertical tangencies. The derivations, error bound, continuity conditions, and sampling limitations are documented in docs/math/interpolation.md.

Exporters and adapters

from bezierkit.export.json import dumps, loads
from bezierkit.export.svg import to_svg_path_data
from bezierkit.export.tikz import to_tikz

json_document = dumps(path, metadata={"name": "indifference-curve"})
same_path = loads(json_document).path
svg_path_data = to_svg_path_data(path, precision=5)
tikz = to_tikz(path, precision=5, options="thick")

TikZ uses native .. controls ... and ... .. commands and SVG uses native C commands; neither exporter flattens cubic geometry or chooses a theme. The versioned JSON schema preserves all controls, subpaths, closure, dimension, and caller metadata.

Matplotlib interoperability is optional:

uv add "bezierkit[matplotlib]"
from bezierkit.adapters.matplotlib import from_path, to_path

geometry = from_path(matplotlib_path, transform=affine_transform)
round_trip = to_path(geometry)

Affine transforms preserve controls exactly. Non-affine transforms require the explicit approximate_path(..., tolerance=...) API. Format guarantees and round-trip tolerances are documented in docs/exporters.md.

Command-line interface

Run bezierkit directly if it's installed in your active environment, or prefix every command with uv run (e.g. uv run bezierkit --help) when working inside a uv project without activating the venv.

Evaluate a curve or derivative:

bezierkit evaluate \
  --points "0,0" --points "1,2" --points "3,2" --points "4,0" \
  --t 0:1:0.25 --order 1 --json

Sample as CSV:

bezierkit sample \
  --points "0,0" --points "1,2" --points "3,2" --points "4,0" \
  --count 50 --format csv > curve.csv

Construct a curve from planar slopes and pipe its control points directly into another command:

bezierkit construct slopes \
  --start "0,5" --end "5,0" \
  --start-slope -2 --end-slope -0.3 \
| bezierkit sample --count 20 --format json

Run bezierkit --help for the complete command tree. Expected domain and input errors are printed without a traceback; add the global --debug option before the subcommand when diagnosing unexpected failures.

Scope

Version 1.0 is the first stable release: the public API listed in bezierkit.__all__ and the subpackages documented above follow semantic versioning from here on, and the JSON path schema stays at version 1. See CHANGELOG.md for the release history. Rendering style and diagram semantics remain intentionally outside this package. Intersections, B-splines, and NURBS are future work.

Metadata

Release files for bezierkit 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bezierkit 1.0.0
File Size Uploaded
bezierkit-1.0.0.tar.gz 169.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bezierkit 1.0.0
File Interpreter ABI Platform
bezierkit-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 221.5 kB

Release files / bezierkit-1.0.0.tar.gz

Download URL bezierkit-1.0.0.tar.gz
Size 169.0 kB
Tags Source
SHA-256 checksum
How to use checksums
309fa0984a6cee703342c8e133a2983725e3bd6c12aca725d48decfe877a4060
BLAKE2b-256 checksum
How to use checksums
958495fb05e260c1c214c0d69cd5f80c235d87075bd30b8306c94cf928c4d52b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bezierkit-1.0.0-py3-none-any.whl

Download URL bezierkit-1.0.0-py3-none-any.whl
Size 52.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42542e262c3829ce422fb3968b5d91049a02d12257611d9ef51ca5dc107eb553
BLAKE2b-256 checksum
How to use checksums
b0bf899871c291ad2ead783008f085fa995096bac09c768a85a4c2b371ec11e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

1.0.1

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page