bezierkit 0.5.0rc1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bezierkit-0.5.0rc1/.github/workflows/publish.yml +30 -0
- bezierkit-0.5.0rc1/.github/workflows/test.yml +24 -0
- bezierkit-0.5.0rc1/.gitignore +16 -0
- bezierkit-0.5.0rc1/.import_linter_cache/.gitignore +2 -0
- bezierkit-0.5.0rc1/.import_linter_cache/CACHEDIR.TAG +3 -0
- bezierkit-0.5.0rc1/.import_linter_cache/a9abca5d6e0845446e009b50919d871bac847893.data.json +1 -0
- bezierkit-0.5.0rc1/.import_linter_cache/bezierkit.meta.json +1 -0
- bezierkit-0.5.0rc1/.pre-commit-config.yaml +26 -0
- bezierkit-0.5.0rc1/LICENSE +21 -0
- bezierkit-0.5.0rc1/PKG-INFO +258 -0
- bezierkit-0.5.0rc1/README.md +229 -0
- bezierkit-0.5.0rc1/benchmarks/README.md +35 -0
- bezierkit-0.5.0rc1/benchmarks/benchmark_evaluation.py +82 -0
- bezierkit-0.5.0rc1/benchmarks/benchmark_polyline.py +54 -0
- bezierkit-0.5.0rc1/benchmarks/results/macos-arm64-python3.13.json +32 -0
- bezierkit-0.5.0rc1/benchmarks/results/polyline-macos-arm64-python3.13.json +23 -0
- bezierkit-0.5.0rc1/docs/assets/banner.svg +23 -0
- bezierkit-0.5.0rc1/docs/exporters.md +40 -0
- bezierkit-0.5.0rc1/docs/formats/json-path-v1.md +37 -0
- bezierkit-0.5.0rc1/docs/math/interpolation.md +74 -0
- bezierkit-0.5.0rc1/pyproject.toml +107 -0
- bezierkit-0.5.0rc1/src/bezierkit/__init__.py +36 -0
- bezierkit-0.5.0rc1/src/bezierkit/adapters/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/adapters/matplotlib.py +223 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/__init__.py +16 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/basis/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/basis/bernstein.py +43 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/basis/binomial.py +13 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/conversion.py +41 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/curve.py +104 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/evaluation/__init__.py +6 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/evaluation/bernstein.py +23 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/evaluation/casteljau.py +24 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/evaluation/evaluator.py +14 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/operations/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/operations/hodograph.py +15 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/operations/subdivision.py +42 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/path.py +221 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/polygon.py +42 -0
- bezierkit-0.5.0rc1/src/bezierkit/bezier/segment.py +134 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/__main__.py +4 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/app.py +47 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/commands/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/commands/construct.py +61 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/commands/evaluate.py +36 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/commands/sample.py +55 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/formatting.py +42 -0
- bezierkit-0.5.0rc1/src/bezierkit/cli/parsing.py +82 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/__init__.py +7 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/construction.py +11 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/endpoint/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/endpoint/derivatives.py +37 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/endpoint/tangents.py +47 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/planar/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/construction/planar/slopes.py +39 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/capabilities/__init__.py +8 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/capabilities/curve.py +31 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/capabilities/differentiable.py +10 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/capabilities/reversible.py +10 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/capabilities/subdividable.py +13 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/domain/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/domain/interval.py +29 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/domain/parameter.py +47 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/errors.py +18 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/geometry/__init__.py +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/geometry/point.py +70 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/geometry/point_set.py +72 -0
- bezierkit-0.5.0rc1/src/bezierkit/core/geometry/vector.py +63 -0
- bezierkit-0.5.0rc1/src/bezierkit/export/__init__.py +13 -0
- bezierkit-0.5.0rc1/src/bezierkit/export/_coordinates.py +28 -0
- bezierkit-0.5.0rc1/src/bezierkit/export/json.py +108 -0
- bezierkit-0.5.0rc1/src/bezierkit/export/svg.py +98 -0
- bezierkit-0.5.0rc1/src/bezierkit/export/tikz.py +55 -0
- bezierkit-0.5.0rc1/src/bezierkit/fitting/__init__.py +12 -0
- bezierkit-0.5.0rc1/src/bezierkit/fitting/adaptive.py +115 -0
- bezierkit-0.5.0rc1/src/bezierkit/fitting/polyline.py +137 -0
- bezierkit-0.5.0rc1/src/bezierkit/implicit/__init__.py +5 -0
- bezierkit-0.5.0rc1/src/bezierkit/implicit/trace.py +270 -0
- bezierkit-0.5.0rc1/src/bezierkit/interpolation/__init__.py +5 -0
- bezierkit-0.5.0rc1/src/bezierkit/interpolation/hermite.py +57 -0
- bezierkit-0.5.0rc1/src/bezierkit/py.typed +1 -0
- bezierkit-0.5.0rc1/src/bezierkit/sampling/__init__.py +6 -0
- bezierkit-0.5.0rc1/src/bezierkit/sampling/sample.py +49 -0
- bezierkit-0.5.0rc1/src/bezierkit/sampling/sampler.py +13 -0
- bezierkit-0.5.0rc1/src/bezierkit/sampling/uniform.py +22 -0
- bezierkit-0.5.0rc1/tests/adapters/test_matplotlib.py +89 -0
- bezierkit-0.5.0rc1/tests/bezier/basis/test_bernstein.py +28 -0
- bezierkit-0.5.0rc1/tests/bezier/basis/test_binomial.py +13 -0
- bezierkit-0.5.0rc1/tests/bezier/evaluation/test_bernstein_evaluator.py +21 -0
- bezierkit-0.5.0rc1/tests/bezier/evaluation/test_casteljau.py +32 -0
- bezierkit-0.5.0rc1/tests/bezier/operations/test_hodograph.py +24 -0
- bezierkit-0.5.0rc1/tests/bezier/operations/test_subdivision.py +44 -0
- bezierkit-0.5.0rc1/tests/bezier/test_conversion.py +33 -0
- bezierkit-0.5.0rc1/tests/bezier/test_curve.py +107 -0
- bezierkit-0.5.0rc1/tests/bezier/test_path.py +73 -0
- bezierkit-0.5.0rc1/tests/bezier/test_polygon.py +34 -0
- bezierkit-0.5.0rc1/tests/bezier/test_segment.py +75 -0
- bezierkit-0.5.0rc1/tests/cli/test_app.py +73 -0
- bezierkit-0.5.0rc1/tests/cli/test_construct.py +55 -0
- bezierkit-0.5.0rc1/tests/cli/test_evaluate.py +45 -0
- bezierkit-0.5.0rc1/tests/cli/test_parsing.py +40 -0
- bezierkit-0.5.0rc1/tests/cli/test_sample.py +76 -0
- bezierkit-0.5.0rc1/tests/conformance/test_exporters.py +86 -0
- bezierkit-0.5.0rc1/tests/construction/endpoint/test_derivatives.py +29 -0
- bezierkit-0.5.0rc1/tests/construction/endpoint/test_tangents.py +46 -0
- bezierkit-0.5.0rc1/tests/construction/planar/test_slopes.py +34 -0
- bezierkit-0.5.0rc1/tests/construction/test_construction.py +8 -0
- bezierkit-0.5.0rc1/tests/core/capabilities/test_capabilities.py +41 -0
- bezierkit-0.5.0rc1/tests/core/domain/test_interval.py +30 -0
- bezierkit-0.5.0rc1/tests/core/domain/test_parameter.py +33 -0
- bezierkit-0.5.0rc1/tests/core/geometry/test_point.py +50 -0
- bezierkit-0.5.0rc1/tests/core/geometry/test_point_set.py +47 -0
- bezierkit-0.5.0rc1/tests/core/geometry/test_vector.py +36 -0
- bezierkit-0.5.0rc1/tests/core/test_errors.py +21 -0
- bezierkit-0.5.0rc1/tests/export/test_json.py +53 -0
- bezierkit-0.5.0rc1/tests/export/test_svg.py +45 -0
- bezierkit-0.5.0rc1/tests/export/test_tikz.py +42 -0
- bezierkit-0.5.0rc1/tests/fitting/test_adaptive.py +52 -0
- bezierkit-0.5.0rc1/tests/fitting/test_polyline.py +28 -0
- bezierkit-0.5.0rc1/tests/implicit/test_trace.py +67 -0
- bezierkit-0.5.0rc1/tests/interpolation/test_hermite.py +49 -0
- bezierkit-0.5.0rc1/tests/sampling/test_sample.py +40 -0
- bezierkit-0.5.0rc1/tests/sampling/test_uniform.py +42 -0
- bezierkit-0.5.0rc1/tests/test_public_api.py +41 -0
- bezierkit-0.5.0rc1/uv.lock +1629 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # required for PyPI Trusted Publishing (OIDC)
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: astral-sh/setup-uv@v3
|
|
17
|
+
- run: uv python install 3.12
|
|
18
|
+
|
|
19
|
+
- name: Run test suite
|
|
20
|
+
run: |
|
|
21
|
+
uv sync --all-extras --dev
|
|
22
|
+
uv run ruff check src tests
|
|
23
|
+
uv run lint-imports
|
|
24
|
+
uv run pytest -v
|
|
25
|
+
|
|
26
|
+
- name: Build distributions
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Publish to PyPI
|
|
30
|
+
run: uv publish
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
enable-cache: true
|
|
20
|
+
- run: uv sync --all-extras --dev
|
|
21
|
+
- run: uv run ruff check src tests benchmarks
|
|
22
|
+
- run: uv run lint-imports
|
|
23
|
+
- run: uv run pytest -v
|
|
24
|
+
- run: uv build
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"imports_by_module":{"bezierkit":[["bezierkit.bezier.curve",false,5,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.bezier.segment",false,7,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.core.geometry.point",false,15,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.geometry.vector",false,16,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.core.errors",false,8,"from bezierkit.core.errors import ("],["bezierkit.bezier.path",false,6,"from bezierkit.bezier.path import BezierSubpath, PiecewiseBezier"]],"bezierkit.adapters":[],"bezierkit.adapters.matplotlib":[["bezierkit.fitting.polyline",false,12,"from bezierkit.fitting.polyline import fit_polyline"],["bezierkit.bezier.conversion",false,8,"from bezierkit.bezier.conversion import line_to_cubic, quadratic_to_cubic"],["bezierkit.bezier.path",false,9,"from bezierkit.bezier.path import BezierSubpath, PiecewiseBezier"],["bezierkit.core.geometry.point",false,11,"from bezierkit.core.geometry.point import Point"],["bezierkit.bezier.segment",false,10,"from bezierkit.bezier.segment import CubicBezierSegment"]],"bezierkit.bezier":[["bezierkit.bezier.segment",false,6,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.bezier.conversion",false,3,"from bezierkit.bezier.conversion import line_to_cubic, quadratic_to_cubic, to_cubic"],["bezierkit.bezier.path",false,5,"from bezierkit.bezier.path import BezierSubpath, PiecewiseBezier"],["bezierkit.bezier.curve",false,4,"from bezierkit.bezier.curve import BezierCurve"]],"bezierkit.bezier.basis":[],"bezierkit.bezier.basis.bernstein":[["bezierkit.bezier.basis.binomial",false,7,"from bezierkit.bezier.basis.binomial import BinomialTable"]],"bezierkit.bezier.basis.binomial":[],"bezierkit.bezier.conversion":[["bezierkit.core.errors",false,5,"from bezierkit.core.errors import DegreeError, DimensionMismatch"],["bezierkit.bezier.curve",false,3,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.bezier.segment",false,4,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.core.geometry.point",false,6,"from bezierkit.core.geometry.point import Point"]],"bezierkit.bezier.curve":[["bezierkit.bezier.operations.subdivision",false,10,"from bezierkit.bezier.operations.subdivision import Subdivider"],["bezierkit.core.errors",false,18,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.core.capabilities.differentiable",false,13,"from bezierkit.core.capabilities.differentiable import Differentiable"],["bezierkit.core.capabilities.reversible",false,14,"from bezierkit.core.capabilities.reversible import Reversible"],["bezierkit.core.geometry.point",false,19,"from bezierkit.core.geometry.point import Point"],["bezierkit.bezier.evaluation.casteljau",false,7,"from bezierkit.bezier.evaluation.casteljau import DeCasteljauEvaluator"],["bezierkit.bezier.evaluation.evaluator",false,8,"from bezierkit.bezier.evaluation.evaluator import Evaluator"],["bezierkit.bezier.polygon",false,11,"from bezierkit.bezier.polygon import ControlPolygon"],["bezierkit.core.geometry.point_set",false,20,"from bezierkit.core.geometry.point_set import PointSet"],["bezierkit.core.domain.interval",false,16,"from bezierkit.core.domain.interval import Interval"],["bezierkit.bezier.operations.hodograph",false,9,"from bezierkit.bezier.operations.hodograph import Hodograph"],["bezierkit.core.capabilities.subdividable",false,15,"from bezierkit.core.capabilities.subdividable import Subdividable"],["bezierkit.core.domain.parameter",false,17,"from bezierkit.core.domain.parameter import ParameterValues"],["bezierkit.core.capabilities.curve",false,12,"from bezierkit.core.capabilities.curve import ParametricCurve"]],"bezierkit.bezier.evaluation":[["bezierkit.bezier.evaluation.bernstein",false,2,"from bezierkit.bezier.evaluation.bernstein import BernsteinEvaluator"],["bezierkit.bezier.evaluation.casteljau",false,3,"from bezierkit.bezier.evaluation.casteljau import DeCasteljauEvaluator"],["bezierkit.bezier.evaluation.evaluator",false,4,"from bezierkit.bezier.evaluation.evaluator import Evaluator"]],"bezierkit.bezier.evaluation.bernstein":[["bezierkit.bezier.evaluation.evaluator",false,6,"from bezierkit.bezier.evaluation.evaluator import Evaluator"],["bezierkit.bezier.basis.bernstein",false,5,"from bezierkit.bezier.basis.bernstein import BernsteinBasis"],["bezierkit.bezier.polygon",false,7,"from bezierkit.bezier.polygon import ControlPolygon"]],"bezierkit.bezier.evaluation.casteljau":[["bezierkit.bezier.evaluation.evaluator",false,5,"from bezierkit.bezier.evaluation.evaluator import Evaluator"],["bezierkit.bezier.polygon",false,6,"from bezierkit.bezier.polygon import ControlPolygon"]],"bezierkit.bezier.evaluation.evaluator":[["bezierkit.bezier.polygon",false,7,"from bezierkit.bezier.polygon import ControlPolygon"]],"bezierkit.bezier.operations":[],"bezierkit.bezier.operations.hodograph":[["bezierkit.bezier.polygon",false,5,"from bezierkit.bezier.polygon import ControlPolygon"]],"bezierkit.bezier.operations.subdivision":[["bezierkit.bezier.polygon",false,5,"from bezierkit.bezier.polygon import ControlPolygon"],["bezierkit.core.errors",false,6,"from bezierkit.core.errors import ParameterOutOfDomain"]],"bezierkit.bezier.path":[["bezierkit.core.capabilities.reversible",false,11,"from bezierkit.core.capabilities.reversible import Reversible"],["bezierkit.core.capabilities.curve",false,10,"from bezierkit.core.capabilities.curve import ParametricCurve"],["bezierkit.core.domain.interval",false,13,"from bezierkit.core.domain.interval import Interval"],["bezierkit.bezier.segment",false,9,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.core.geometry.point_set",false,17,"from bezierkit.core.geometry.point_set import PointSet"],["bezierkit.core.geometry.point",false,16,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.capabilities.subdividable",false,12,"from bezierkit.core.capabilities.subdividable import Subdividable"],["bezierkit.core.errors",false,15,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.core.domain.parameter",false,14,"from bezierkit.core.domain.parameter import ParameterValues"]],"bezierkit.bezier.polygon":[["bezierkit.core.errors",false,9,"from bezierkit.core.errors import DegreeError"],["bezierkit.core.geometry.point_set",false,11,"from bezierkit.core.geometry.point_set import PointSet"],["bezierkit.core.geometry.point",false,10,"from bezierkit.core.geometry.point import Point"]],"bezierkit.bezier.segment":[["bezierkit.core.capabilities.curve",false,12,"from bezierkit.core.capabilities.curve import ParametricCurve"],["bezierkit.core.capabilities.differentiable",false,13,"from bezierkit.core.capabilities.differentiable import Differentiable"],["bezierkit.core.capabilities.subdividable",false,15,"from bezierkit.core.capabilities.subdividable import Subdividable"],["bezierkit.bezier.conversion",false,126,"from bezierkit.bezier.conversion import line_to_cubic"],["bezierkit.core.geometry.point_set",false,19,"from bezierkit.core.geometry.point_set import PointSet"],["bezierkit.core.errors",false,17,"from bezierkit.core.errors import DegreeError, DimensionMismatch"],["bezierkit.bezier.evaluation.evaluator",false,11,"from bezierkit.bezier.evaluation.evaluator import Evaluator"],["bezierkit.core.capabilities.reversible",false,14,"from bezierkit.core.capabilities.reversible import Reversible"],["bezierkit.core.geometry.point",false,18,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.domain.interval",false,16,"from bezierkit.core.domain.interval import Interval"],["bezierkit.bezier.curve",false,9,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.bezier.conversion",false,132,"from bezierkit.bezier.conversion import quadratic_to_cubic"],["bezierkit.bezier.evaluation.casteljau",false,10,"from bezierkit.bezier.evaluation.casteljau import DeCasteljauEvaluator"]],"bezierkit.cli":[],"bezierkit.cli.__main__":[["bezierkit.cli.app",false,1,"from bezierkit.cli.app import main"]],"bezierkit.cli.app":[["bezierkit.cli.commands.evaluate",false,9,"from bezierkit.cli.commands.evaluate import app as evaluate_app"],["bezierkit.cli.commands.construct",false,8,"from bezierkit.cli.commands.construct import app as construct_app"],["bezierkit.core.errors",false,11,"from bezierkit.core.errors import BezierKitError"],["bezierkit.cli.commands.sample",false,10,"from bezierkit.cli.commands.sample import app as sample_app"]],"bezierkit.cli.commands":[],"bezierkit.cli.commands.construct":[["bezierkit",false,8,"from bezierkit import BezierCurve"],["bezierkit.cli.parsing",false,9,"from bezierkit.cli.parsing import parse_point, parse_vector"],["bezierkit.construction",false,10,"from bezierkit.construction import PlanarSlopes, TangentDirections"]],"bezierkit.cli.commands.evaluate":[["bezierkit.cli.parsing",false,9,"from bezierkit.cli.parsing import parse_t_values, resolve_control_points"],["bezierkit",false,7,"from bezierkit import BezierCurve"],["bezierkit.cli.formatting",false,8,"from bezierkit.cli.formatting import render_json, render_table"]],"bezierkit.cli.commands.sample":[["bezierkit.sampling",false,11,"from bezierkit.sampling import Sample, UniformSampler"],["bezierkit.cli.formatting",false,9,"from bezierkit.cli.formatting import render_csv, render_json, render_table"],["bezierkit.cli.parsing",false,10,"from bezierkit.cli.parsing import resolve_control_points"],["bezierkit",false,8,"from bezierkit import BezierCurve, BezierKitError"]],"bezierkit.cli.formatting":[],"bezierkit.cli.parsing":[["bezierkit",false,8,"from bezierkit import Point, Vector"],["bezierkit.core.errors",false,9,"from bezierkit.core.errors import BezierKitError"]],"bezierkit.construction":[["bezierkit.construction.endpoint.tangents",false,4,"from bezierkit.construction.endpoint.tangents import TangentDirections"],["bezierkit.construction.planar.slopes",false,5,"from bezierkit.construction.planar.slopes import PlanarSlopes"],["bezierkit.construction.endpoint.derivatives",false,3,"from bezierkit.construction.endpoint.derivatives import EndpointDerivatives"]],"bezierkit.construction.construction":[],"bezierkit.construction.endpoint":[],"bezierkit.construction.endpoint.derivatives":[["bezierkit.bezier.curve",false,5,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.core.geometry.point",false,8,"from bezierkit.core.geometry.point import Point"],["bezierkit.construction.construction",false,6,"from bezierkit.construction.construction import Construction"],["bezierkit.core.errors",false,7,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.core.geometry.vector",false,9,"from bezierkit.core.geometry.vector import Vector"]],"bezierkit.construction.endpoint.tangents":[["bezierkit.bezier.curve",false,6,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.core.errors",false,9,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.core.geometry.point",false,10,"from bezierkit.core.geometry.point import Point"],["bezierkit.construction.endpoint.derivatives",false,8,"from bezierkit.construction.endpoint.derivatives import EndpointDerivatives"],["bezierkit.core.geometry.vector",false,11,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.construction.construction",false,7,"from bezierkit.construction.construction import Construction"]],"bezierkit.construction.planar":[],"bezierkit.construction.planar.slopes":[["bezierkit.core.errors",false,9,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.construction.construction",false,7,"from bezierkit.construction.construction import Construction"],["bezierkit.core.geometry.vector",false,11,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.bezier.curve",false,6,"from bezierkit.bezier.curve import BezierCurve"],["bezierkit.core.geometry.point",false,10,"from bezierkit.core.geometry.point import Point"],["bezierkit.construction.endpoint.tangents",false,8,"from bezierkit.construction.endpoint.tangents import TangentDirections"]],"bezierkit.core":[],"bezierkit.core.capabilities":[["bezierkit.core.capabilities.reversible",false,5,"from bezierkit.core.capabilities.reversible import Reversible"],["bezierkit.core.capabilities.differentiable",false,4,"from bezierkit.core.capabilities.differentiable import Differentiable"],["bezierkit.core.capabilities.subdividable",false,6,"from bezierkit.core.capabilities.subdividable import Subdividable"],["bezierkit.core.capabilities.curve",false,3,"from bezierkit.core.capabilities.curve import ParametricCurve"]],"bezierkit.core.capabilities.curve":[["bezierkit.core.domain.interval",false,8,"from bezierkit.core.domain.interval import Interval"],["bezierkit.core.geometry.point",false,9,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.geometry.point_set",false,10,"from bezierkit.core.geometry.point_set import PointSet"]],"bezierkit.core.capabilities.differentiable":[],"bezierkit.core.capabilities.reversible":[],"bezierkit.core.capabilities.subdividable":[],"bezierkit.core.domain":[],"bezierkit.core.domain.interval":[],"bezierkit.core.domain.parameter":[["bezierkit.core.errors",false,9,"from bezierkit.core.errors import ParameterOutOfDomain"],["bezierkit.core.domain.interval",false,8,"from bezierkit.core.domain.interval import Interval"]],"bezierkit.core.errors":[],"bezierkit.core.geometry":[],"bezierkit.core.geometry.point":[["bezierkit.core.geometry.vector",false,12,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.core.errors",false,9,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.core.geometry.vector",false,62,"from bezierkit.core.geometry.vector import Vector"]],"bezierkit.core.geometry.point_set":[["bezierkit.core.geometry.point",false,9,"from bezierkit.core.geometry.point import Point"]],"bezierkit.core.geometry.vector":[["bezierkit.core.errors",false,8,"from bezierkit.core.errors import DimensionMismatch"]],"bezierkit.export":[["bezierkit.export.tikz",false,5,"from bezierkit.export.tikz import segment_to_tikz, to_tikz"],["bezierkit.export.json",false,3,"from bezierkit.export.json import PathDocument"],["bezierkit.export.svg",false,4,"from bezierkit.export.svg import from_svg_path_data, to_svg_path_data"]],"bezierkit.export._coordinates":[["bezierkit.core.geometry.point",false,6,"from bezierkit.core.geometry.point import Point"]],"bezierkit.export.json":[["bezierkit.bezier.segment",false,10,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.bezier.path",false,9,"from bezierkit.bezier.path import BezierSubpath, PiecewiseBezier"],["bezierkit.core.geometry.point",false,11,"from bezierkit.core.geometry.point import Point"]],"bezierkit.export.svg":[["bezierkit.bezier.path",false,6,"from bezierkit.bezier.path import BezierSubpath, PiecewiseBezier"],["bezierkit.bezier.segment",false,7,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.export._coordinates",false,9,"from bezierkit.export._coordinates import CoordinateTransform, number, transformed"],["bezierkit.core.geometry.point",false,8,"from bezierkit.core.geometry.point import Point"]],"bezierkit.export.tikz":[["bezierkit.bezier.segment",false,4,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.export._coordinates",false,6,"from bezierkit.export._coordinates import CoordinateTransform, number, transformed"],["bezierkit.bezier.path",false,3,"from bezierkit.bezier.path import PiecewiseBezier"],["bezierkit.core.geometry.point",false,5,"from bezierkit.core.geometry.point import Point"]],"bezierkit.fitting":[["bezierkit.fitting.adaptive",false,3,"from bezierkit.fitting.adaptive import fit_graph, fit_parametric, hermite_error_bound"],["bezierkit.fitting.polyline",false,4,"from bezierkit.fitting.polyline import fit_polyline, maximum_polyline_deviation"]],"bezierkit.fitting.adaptive":[["bezierkit.bezier.segment",false,9,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.bezier.path",false,8,"from bezierkit.bezier.path import PiecewiseBezier"],["bezierkit.core.errors",false,10,"from bezierkit.core.errors import ToleranceNotMet"],["bezierkit.interpolation.hermite",false,13,"from bezierkit.interpolation.hermite import parametric_hermite"],["bezierkit.core.geometry.point",false,11,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.geometry.vector",false,12,"from bezierkit.core.geometry.vector import Vector"]],"bezierkit.fitting.polyline":[["bezierkit.bezier.path",false,8,"from bezierkit.bezier.path import PiecewiseBezier"],["bezierkit.bezier.segment",false,9,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.core.geometry.point",false,10,"from bezierkit.core.geometry.point import Point"]],"bezierkit.implicit":[["bezierkit.implicit.trace",false,3,"from bezierkit.implicit.trace import ContourSet, LevelContours, trace_implicit"]],"bezierkit.implicit.trace":[["bezierkit.bezier.segment",false,11,"from bezierkit.bezier.segment import CubicBezierSegment"],["bezierkit.core.geometry.vector",false,13,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.core.geometry.point",false,12,"from bezierkit.core.geometry.point import Point"],["bezierkit.fitting.polyline",false,14,"from bezierkit.fitting.polyline import fit_polyline"],["bezierkit.bezier.path",false,10,"from bezierkit.bezier.path import PiecewiseBezier"]],"bezierkit.interpolation":[["bezierkit.interpolation.hermite",false,3,"from bezierkit.interpolation.hermite import graph_hermite, parametric_hermite"]],"bezierkit.interpolation.hermite":[["bezierkit.core.geometry.point",false,7,"from bezierkit.core.geometry.point import Point"],["bezierkit.core.geometry.vector",false,8,"from bezierkit.core.geometry.vector import Vector"],["bezierkit.core.errors",false,6,"from bezierkit.core.errors import DimensionMismatch"],["bezierkit.bezier.segment",false,5,"from bezierkit.bezier.segment import CubicBezierSegment"]],"bezierkit.sampling":[["bezierkit.sampling.uniform",false,4,"from bezierkit.sampling.uniform import UniformSampler"],["bezierkit.sampling.sample",false,3,"from bezierkit.sampling.sample import Sample"]],"bezierkit.sampling.sample":[["bezierkit.core.geometry.point_set",false,9,"from bezierkit.core.geometry.point_set import PointSet"],["bezierkit.core.geometry.point",false,8,"from bezierkit.core.geometry.point import Point"]],"bezierkit.sampling.sampler":[["bezierkit.core.capabilities.curve",false,5,"from bezierkit.core.capabilities.curve import ParametricCurve"],["bezierkit.sampling.sample",false,6,"from bezierkit.sampling.sample import Sample"]],"bezierkit.sampling.uniform":[["bezierkit.sampling.sampler",false,7,"from bezierkit.sampling.sampler import Sampler"],["bezierkit.core.capabilities.curve",false,5,"from bezierkit.core.capabilities.curve import ParametricCurve"],["bezierkit.sampling.sample",false,6,"from bezierkit.sampling.sample import Sample"]]},"version":2}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"bezierkit.bezier.evaluation": 1790608899.2898939, "bezierkit": 1790608899.2895749, "bezierkit.core.errors": 1790608899.291696, "bezierkit.export": 1790608899.2918577, "bezierkit.bezier": 1790608899.2896984, "bezierkit.export.tikz": 1790608899.2920394, "bezierkit.bezier.segment": 1790608899.2903366, "bezierkit.implicit.trace": 1790608899.2924604, "bezierkit.construction.endpoint.derivatives": 1790608899.291118, "bezierkit.sampling": 1790608899.2926018, "bezierkit.core.capabilities.differentiable": 1790608899.2914712, "bezierkit.bezier.operations": 1790608899.2901056, "bezierkit.bezier.conversion": 1790608899.2898939, "bezierkit.bezier.polygon": 1790608899.2903366, "bezierkit.sampling.uniform": 1790608899.2927358, "bezierkit.cli": 1790608899.2903366, "bezierkit.core.capabilities.curve": 1790608899.2914712, "bezierkit.construction.endpoint.tangents": 1790608899.291118, "bezierkit.bezier.basis": 1790608899.2898448, "bezierkit.core.domain.parameter": 1790608899.291696, "bezierkit.export.svg": 1790608899.2920394, "bezierkit.core.domain.interval": 1790608899.291696, "bezierkit.construction.planar": 1790608899.291118, "bezierkit.adapters.matplotlib": 1790608899.2896984, "bezierkit.bezier.operations.subdivision": 1790608899.2903366, "bezierkit.export.json": 1790608899.2920394, "bezierkit.sampling.sample": 1790608899.2927358, "bezierkit.core.geometry.point_set": 1790608899.2918577, "bezierkit.cli.formatting": 1790608899.2907736, "bezierkit.bezier.evaluation.bernstein": 1790608899.2901056, "bezierkit.adapters": 1790608899.2896466, "bezierkit.core": 1790608899.2913053, "bezierkit.core.geometry.vector": 1790608899.2918577, "bezierkit.export._coordinates": 1790608899.2920394, "bezierkit.cli.app": 1790608899.2906537, "bezierkit.bezier.basis.bernstein": 1790608899.2898939, "bezierkit.bezier.basis.binomial": 1790608899.2898939, "bezierkit.cli.commands.construct": 1790608899.2907736, "bezierkit.construction": 1790608899.2907736, "bezierkit.cli.parsing": 1790608899.2907736, "bezierkit.fitting.adaptive": 1790608899.2923026, "bezierkit.construction.construction": 1790608899.2910295, "bezierkit.fitting": 1790608899.2920394, "bezierkit.cli.__main__": 1790608899.2906537, "bezierkit.bezier.path": 1790608899.2903366, "bezierkit.bezier.evaluation.evaluator": 1790608899.2901056, "bezierkit.core.capabilities.reversible": 1790608899.2914712, "bezierkit.bezier.curve": 1790608899.2898939, "bezierkit.core.domain": 1790608899.2914712, "bezierkit.implicit": 1790608899.2923026, "bezierkit.core.capabilities.subdividable": 1790608899.2914712, "bezierkit.bezier.operations.hodograph": 1790608899.2903366, "bezierkit.fitting.polyline": 1790608899.2923026, "bezierkit.interpolation": 1790608899.2924604, "bezierkit.core.capabilities": 1790608899.291418, "bezierkit.construction.endpoint": 1790608899.2910295, "bezierkit.core.geometry.point": 1790608899.2918577, "bezierkit.interpolation.hermite": 1790608899.2926018, "bezierkit.sampling.sampler": 1790608899.2927358, "bezierkit.bezier.evaluation.casteljau": 1790608899.2901056, "bezierkit.construction.planar.slopes": 1790608899.2913053, "bezierkit.cli.commands": 1790608899.2906537, "bezierkit.cli.commands.sample": 1790608899.2907736, "bezierkit.core.geometry": 1790608899.291696, "bezierkit.cli.commands.evaluate": 1790608899.2907736}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: uv-lock
|
|
5
|
+
name: uv lock --check
|
|
6
|
+
entry: uv lock --check
|
|
7
|
+
language: system
|
|
8
|
+
pass_filenames: false
|
|
9
|
+
|
|
10
|
+
- id: ruff-check
|
|
11
|
+
name: ruff check
|
|
12
|
+
entry: uv run ruff check src tests benchmarks
|
|
13
|
+
language: system
|
|
14
|
+
pass_filenames: false
|
|
15
|
+
|
|
16
|
+
- id: import-contracts
|
|
17
|
+
name: import-linter contracts
|
|
18
|
+
entry: uv run lint-imports
|
|
19
|
+
language: system
|
|
20
|
+
pass_filenames: false
|
|
21
|
+
|
|
22
|
+
- id: pytest
|
|
23
|
+
name: pytest
|
|
24
|
+
entry: uv run pytest -q
|
|
25
|
+
language: system
|
|
26
|
+
pass_filenames: false
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EconViz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bezierkit
|
|
3
|
+
Version: 0.5.0rc1
|
|
4
|
+
Summary: A small mathematical toolkit for constructing and analyzing Bezier curves.
|
|
5
|
+
Project-URL: Homepage, https://github.com/EconViz/bezierkit
|
|
6
|
+
Project-URL: Repository, https://github.com/EconViz/bezierkit
|
|
7
|
+
Project-URL: Issues, https://github.com/EconViz/bezierkit/issues
|
|
8
|
+
Author: EconViz
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Provides-Extra: cli
|
|
24
|
+
Requires-Dist: rich>=13; extra == 'cli'
|
|
25
|
+
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
26
|
+
Provides-Extra: matplotlib
|
|
27
|
+
Requires-Dist: matplotlib>=3.8; extra == 'matplotlib'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# bezierkit
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="docs/assets/banner.svg" alt="bezierkit" width="480">
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<a href="https://pypi.org/project/bezierkit/"><img alt="PyPI" src="https://img.shields.io/pypi/v/bezierkit?style=flat-square&color=181818&labelColor=f3f3f3&cacheSeconds=300"></a>
|
|
38
|
+
<a href="https://pypi.org/project/bezierkit/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/bezierkit?style=flat-square&color=181818&labelColor=f3f3f3"></a>
|
|
39
|
+
<a href="https://opensource.org/licenses/MIT"><img alt="License" src="https://img.shields.io/badge/License-MIT-181818?style=flat-square&color=181818&labelColor=f3f3f3"></a>
|
|
40
|
+
<img alt="Tests" src="https://img.shields.io/badge/tests-176%20passed-181818?style=flat-square&color=181818&labelColor=f3f3f3">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
A small mathematical toolkit for constructing and analyzing Bézier curves.
|
|
44
|
+
|
|
45
|
+
`bezierkit` is renderer-independent: it provides curve construction,
|
|
46
|
+
evaluation, subdivision, and sampling while leaving plotting to consumers such
|
|
47
|
+
as EconViz, Matplotlib, Plotly, SVG, or Typst adapters.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
Add the library to your project with [uv](https://docs.astral.sh/uv/):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv add bezierkit
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Add the optional command-line interface:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv add "bezierkit[cli]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For development, clone the repo and sync all extras:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv sync --all-extras --dev
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Run anything inside the environment with `uv run`, e.g. `uv run pytest` or
|
|
70
|
+
`uv run bezierkit --help`. A plain `pip install bezierkit` /
|
|
71
|
+
`pip install "bezierkit[cli]"` also works if you are not using uv.
|
|
72
|
+
|
|
73
|
+
## Python API
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from bezierkit import BezierCurve, CubicBezierSegment, PiecewiseBezier, Point
|
|
77
|
+
from bezierkit.construction import PlanarSlopes
|
|
78
|
+
from bezierkit.sampling import UniformSampler
|
|
79
|
+
|
|
80
|
+
curve = BezierCurve.cubic(
|
|
81
|
+
Point(0, 0),
|
|
82
|
+
Point(1, 2),
|
|
83
|
+
Point(3, 2),
|
|
84
|
+
Point(4, 0),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
segment = CubicBezierSegment(
|
|
88
|
+
Point(0, 0),
|
|
89
|
+
Point(1, 2),
|
|
90
|
+
Point(3, 2),
|
|
91
|
+
Point(4, 0),
|
|
92
|
+
)
|
|
93
|
+
segment.control_points # (P0, P1, P2, P3)
|
|
94
|
+
|
|
95
|
+
path = PiecewiseBezier([
|
|
96
|
+
CubicBezierSegment.from_line(Point(0, 0), Point(2, 0)),
|
|
97
|
+
CubicBezierSegment.from_line(Point(2, 0), Point(2, 4)),
|
|
98
|
+
])
|
|
99
|
+
path.at(0.75) # Point(2.0, 2.0)
|
|
100
|
+
|
|
101
|
+
curve.at(0.5) # Point(2.0, 1.5)
|
|
102
|
+
curve.derivative().at(0.5) # first derivative
|
|
103
|
+
left, right = curve.split(0.3)
|
|
104
|
+
sample = UniformSampler(200).sample(curve)
|
|
105
|
+
|
|
106
|
+
demand = PlanarSlopes(
|
|
107
|
+
start=Point(0, 5),
|
|
108
|
+
end=Point(5, 0),
|
|
109
|
+
start_slope=-2,
|
|
110
|
+
end_slope=-0.3,
|
|
111
|
+
).build()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The engine supports arbitrary degree and dimension. `Point`, `Vector`,
|
|
115
|
+
`PointSet`, control polygons, and sampling results are immutable value objects.
|
|
116
|
+
|
|
117
|
+
`CubicBezierSegment` is the primary renderer-facing value object. It exposes
|
|
118
|
+
`p0`, `p1`, `p2`, `p3`, tight axis-aligned bounds, and cubic-preserving split
|
|
119
|
+
and reversal operations. Existing `BezierCurve.cubic(...)` code remains valid;
|
|
120
|
+
use `CubicBezierSegment.from_curve(curve)` to migrate without changing any
|
|
121
|
+
control point, or `segment.as_curve()` when a degree-generic API is required.
|
|
122
|
+
|
|
123
|
+
`PiecewiseBezier` divides `[0, 1]` uniformly across the segments of a single
|
|
124
|
+
subpath. Closure is metadata and never inserts a hidden closing segment.
|
|
125
|
+
`PiecewiseBezier.compound(...)` preserves independent subpaths for fills and
|
|
126
|
+
holes; evaluate and split each subpath independently because a compound path
|
|
127
|
+
has no single continuous parameterization. Reversal preserves each subpath;
|
|
128
|
+
splitting is defined only for a single open subpath.
|
|
129
|
+
|
|
130
|
+
Linear and quadratic inputs can be elevated without changing their geometry:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from bezierkit.bezier import to_cubic
|
|
134
|
+
|
|
135
|
+
cubic = to_cubic(BezierCurve.quadratic(
|
|
136
|
+
Point(0, 0), Point(3, 6), Point(9, 0)
|
|
137
|
+
))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Batch evaluation uses a vectorized de Casteljau implementation by default.
|
|
141
|
+
`BernsteinEvaluator` is also available from `bezierkit.bezier.evaluation` for
|
|
142
|
+
explicit low-degree throughput tradeoffs; see `benchmarks/` for the
|
|
143
|
+
reproducible 400, 10,000, and 100,000-value benchmark.
|
|
144
|
+
|
|
145
|
+
## Interpolation, fitting, and level sets
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from bezierkit.fitting import fit_graph
|
|
149
|
+
from bezierkit.implicit import trace_implicit
|
|
150
|
+
from bezierkit.interpolation import graph_hermite
|
|
151
|
+
|
|
152
|
+
segment = graph_hermite(
|
|
153
|
+
x0=1, x1=1.5,
|
|
154
|
+
y0=4, y1=8 / 3,
|
|
155
|
+
m0=-8, m1=-32 / 9,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
path = fit_graph(
|
|
159
|
+
lambda x: 4 / x**2,
|
|
160
|
+
lambda x: -8 / x**3,
|
|
161
|
+
x0=0.8, x1=3,
|
|
162
|
+
tolerance=1e-3,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
contours = trace_implicit(
|
|
166
|
+
lambda x, y: x**2 * y,
|
|
167
|
+
levels=[1, 2, 4],
|
|
168
|
+
viewport=(0.5, 4, 0, 6),
|
|
169
|
+
resolution=(121, 121),
|
|
170
|
+
tolerance=0.01,
|
|
171
|
+
gradient=lambda x, y: (2 * x * y, x**2),
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Adaptive fitting reports a measured Euclidean error on every segment and
|
|
176
|
+
raises `ToleranceNotMet` if configured limits prevent the requested tolerance.
|
|
177
|
+
Implicit tracing preserves disconnected and closed components as independent
|
|
178
|
+
paths and uses parametric gradient tangents, including at vertical tangencies.
|
|
179
|
+
The derivations, error bound, continuity conditions, and sampling limitations
|
|
180
|
+
are documented in `docs/math/interpolation.md`.
|
|
181
|
+
|
|
182
|
+
## Exporters and adapters
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from bezierkit.export.json import dumps, loads
|
|
186
|
+
from bezierkit.export.svg import to_svg_path_data
|
|
187
|
+
from bezierkit.export.tikz import to_tikz
|
|
188
|
+
|
|
189
|
+
json_document = dumps(path, metadata={"name": "indifference-curve"})
|
|
190
|
+
same_path = loads(json_document).path
|
|
191
|
+
svg_path_data = to_svg_path_data(path, precision=5)
|
|
192
|
+
tikz = to_tikz(path, precision=5, options="thick")
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
TikZ uses native `.. controls ... and ... ..` commands and SVG uses native
|
|
196
|
+
`C` commands; neither exporter flattens cubic geometry or chooses a theme.
|
|
197
|
+
The versioned JSON schema preserves all controls, subpaths, closure, dimension,
|
|
198
|
+
and caller metadata.
|
|
199
|
+
|
|
200
|
+
Matplotlib interoperability is optional:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
uv add "bezierkit[matplotlib]"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from bezierkit.adapters.matplotlib import from_path, to_path
|
|
208
|
+
|
|
209
|
+
geometry = from_path(matplotlib_path, transform=affine_transform)
|
|
210
|
+
round_trip = to_path(geometry)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Affine transforms preserve controls exactly. Non-affine transforms require
|
|
214
|
+
the explicit `approximate_path(..., tolerance=...)` API. Format guarantees and
|
|
215
|
+
round-trip tolerances are documented in `docs/exporters.md`.
|
|
216
|
+
|
|
217
|
+
## Command-line interface
|
|
218
|
+
|
|
219
|
+
Run `bezierkit` directly if it's installed in your active environment, or
|
|
220
|
+
prefix every command with `uv run` (e.g. `uv run bezierkit --help`) when
|
|
221
|
+
working inside a uv project without activating the venv.
|
|
222
|
+
|
|
223
|
+
Evaluate a curve or derivative:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
bezierkit evaluate \
|
|
227
|
+
--points "0,0" --points "1,2" --points "3,2" --points "4,0" \
|
|
228
|
+
--t 0:1:0.25 --order 1 --json
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Sample as CSV:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
bezierkit sample \
|
|
235
|
+
--points "0,0" --points "1,2" --points "3,2" --points "4,0" \
|
|
236
|
+
--count 50 --format csv > curve.csv
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Construct a curve from planar slopes and pipe its control points directly into
|
|
240
|
+
another command:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
bezierkit construct slopes \
|
|
244
|
+
--start "0,5" --end "5,0" \
|
|
245
|
+
--start-slope -2 --end-slope -0.3 \
|
|
246
|
+
| bezierkit sample --count 20 --format json
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Run `bezierkit --help` for the complete command tree. Expected domain and input
|
|
250
|
+
errors are printed without a traceback; add the global `--debug` option before
|
|
251
|
+
the subcommand when diagnosing unexpected failures.
|
|
252
|
+
|
|
253
|
+
## Scope
|
|
254
|
+
|
|
255
|
+
The 0.5 release adds stable JSON geometry, native TikZ and SVG cubic output,
|
|
256
|
+
and optional Matplotlib Path interoperability. Rendering style and diagram
|
|
257
|
+
semantics remain intentionally outside this package. Intersections, B-splines,
|
|
258
|
+
and NURBS are future work.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# bezierkit
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/assets/banner.svg" alt="bezierkit" width="480">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/bezierkit/"><img alt="PyPI" src="https://img.shields.io/pypi/v/bezierkit?style=flat-square&color=181818&labelColor=f3f3f3&cacheSeconds=300"></a>
|
|
9
|
+
<a href="https://pypi.org/project/bezierkit/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/bezierkit?style=flat-square&color=181818&labelColor=f3f3f3"></a>
|
|
10
|
+
<a href="https://opensource.org/licenses/MIT"><img alt="License" src="https://img.shields.io/badge/License-MIT-181818?style=flat-square&color=181818&labelColor=f3f3f3"></a>
|
|
11
|
+
<img alt="Tests" src="https://img.shields.io/badge/tests-176%20passed-181818?style=flat-square&color=181818&labelColor=f3f3f3">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
A small mathematical toolkit for constructing and analyzing Bézier curves.
|
|
15
|
+
|
|
16
|
+
`bezierkit` is renderer-independent: it provides curve construction,
|
|
17
|
+
evaluation, subdivision, and sampling while leaving plotting to consumers such
|
|
18
|
+
as EconViz, Matplotlib, Plotly, SVG, or Typst adapters.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add the library to your project with [uv](https://docs.astral.sh/uv/):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv add bezierkit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Add the optional command-line interface:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv add "bezierkit[cli]"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For development, clone the repo and sync all extras:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv sync --all-extras --dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Run anything inside the environment with `uv run`, e.g. `uv run pytest` or
|
|
41
|
+
`uv run bezierkit --help`. A plain `pip install bezierkit` /
|
|
42
|
+
`pip install "bezierkit[cli]"` also works if you are not using uv.
|
|
43
|
+
|
|
44
|
+
## Python API
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from bezierkit import BezierCurve, CubicBezierSegment, PiecewiseBezier, Point
|
|
48
|
+
from bezierkit.construction import PlanarSlopes
|
|
49
|
+
from bezierkit.sampling import UniformSampler
|
|
50
|
+
|
|
51
|
+
curve = BezierCurve.cubic(
|
|
52
|
+
Point(0, 0),
|
|
53
|
+
Point(1, 2),
|
|
54
|
+
Point(3, 2),
|
|
55
|
+
Point(4, 0),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
segment = CubicBezierSegment(
|
|
59
|
+
Point(0, 0),
|
|
60
|
+
Point(1, 2),
|
|
61
|
+
Point(3, 2),
|
|
62
|
+
Point(4, 0),
|
|
63
|
+
)
|
|
64
|
+
segment.control_points # (P0, P1, P2, P3)
|
|
65
|
+
|
|
66
|
+
path = PiecewiseBezier([
|
|
67
|
+
CubicBezierSegment.from_line(Point(0, 0), Point(2, 0)),
|
|
68
|
+
CubicBezierSegment.from_line(Point(2, 0), Point(2, 4)),
|
|
69
|
+
])
|
|
70
|
+
path.at(0.75) # Point(2.0, 2.0)
|
|
71
|
+
|
|
72
|
+
curve.at(0.5) # Point(2.0, 1.5)
|
|
73
|
+
curve.derivative().at(0.5) # first derivative
|
|
74
|
+
left, right = curve.split(0.3)
|
|
75
|
+
sample = UniformSampler(200).sample(curve)
|
|
76
|
+
|
|
77
|
+
demand = PlanarSlopes(
|
|
78
|
+
start=Point(0, 5),
|
|
79
|
+
end=Point(5, 0),
|
|
80
|
+
start_slope=-2,
|
|
81
|
+
end_slope=-0.3,
|
|
82
|
+
).build()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The engine supports arbitrary degree and dimension. `Point`, `Vector`,
|
|
86
|
+
`PointSet`, control polygons, and sampling results are immutable value objects.
|
|
87
|
+
|
|
88
|
+
`CubicBezierSegment` is the primary renderer-facing value object. It exposes
|
|
89
|
+
`p0`, `p1`, `p2`, `p3`, tight axis-aligned bounds, and cubic-preserving split
|
|
90
|
+
and reversal operations. Existing `BezierCurve.cubic(...)` code remains valid;
|
|
91
|
+
use `CubicBezierSegment.from_curve(curve)` to migrate without changing any
|
|
92
|
+
control point, or `segment.as_curve()` when a degree-generic API is required.
|
|
93
|
+
|
|
94
|
+
`PiecewiseBezier` divides `[0, 1]` uniformly across the segments of a single
|
|
95
|
+
subpath. Closure is metadata and never inserts a hidden closing segment.
|
|
96
|
+
`PiecewiseBezier.compound(...)` preserves independent subpaths for fills and
|
|
97
|
+
holes; evaluate and split each subpath independently because a compound path
|
|
98
|
+
has no single continuous parameterization. Reversal preserves each subpath;
|
|
99
|
+
splitting is defined only for a single open subpath.
|
|
100
|
+
|
|
101
|
+
Linear and quadratic inputs can be elevated without changing their geometry:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from bezierkit.bezier import to_cubic
|
|
105
|
+
|
|
106
|
+
cubic = to_cubic(BezierCurve.quadratic(
|
|
107
|
+
Point(0, 0), Point(3, 6), Point(9, 0)
|
|
108
|
+
))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Batch evaluation uses a vectorized de Casteljau implementation by default.
|
|
112
|
+
`BernsteinEvaluator` is also available from `bezierkit.bezier.evaluation` for
|
|
113
|
+
explicit low-degree throughput tradeoffs; see `benchmarks/` for the
|
|
114
|
+
reproducible 400, 10,000, and 100,000-value benchmark.
|
|
115
|
+
|
|
116
|
+
## Interpolation, fitting, and level sets
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from bezierkit.fitting import fit_graph
|
|
120
|
+
from bezierkit.implicit import trace_implicit
|
|
121
|
+
from bezierkit.interpolation import graph_hermite
|
|
122
|
+
|
|
123
|
+
segment = graph_hermite(
|
|
124
|
+
x0=1, x1=1.5,
|
|
125
|
+
y0=4, y1=8 / 3,
|
|
126
|
+
m0=-8, m1=-32 / 9,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
path = fit_graph(
|
|
130
|
+
lambda x: 4 / x**2,
|
|
131
|
+
lambda x: -8 / x**3,
|
|
132
|
+
x0=0.8, x1=3,
|
|
133
|
+
tolerance=1e-3,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
contours = trace_implicit(
|
|
137
|
+
lambda x, y: x**2 * y,
|
|
138
|
+
levels=[1, 2, 4],
|
|
139
|
+
viewport=(0.5, 4, 0, 6),
|
|
140
|
+
resolution=(121, 121),
|
|
141
|
+
tolerance=0.01,
|
|
142
|
+
gradient=lambda x, y: (2 * x * y, x**2),
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Adaptive fitting reports a measured Euclidean error on every segment and
|
|
147
|
+
raises `ToleranceNotMet` if configured limits prevent the requested tolerance.
|
|
148
|
+
Implicit tracing preserves disconnected and closed components as independent
|
|
149
|
+
paths and uses parametric gradient tangents, including at vertical tangencies.
|
|
150
|
+
The derivations, error bound, continuity conditions, and sampling limitations
|
|
151
|
+
are documented in `docs/math/interpolation.md`.
|
|
152
|
+
|
|
153
|
+
## Exporters and adapters
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from bezierkit.export.json import dumps, loads
|
|
157
|
+
from bezierkit.export.svg import to_svg_path_data
|
|
158
|
+
from bezierkit.export.tikz import to_tikz
|
|
159
|
+
|
|
160
|
+
json_document = dumps(path, metadata={"name": "indifference-curve"})
|
|
161
|
+
same_path = loads(json_document).path
|
|
162
|
+
svg_path_data = to_svg_path_data(path, precision=5)
|
|
163
|
+
tikz = to_tikz(path, precision=5, options="thick")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
TikZ uses native `.. controls ... and ... ..` commands and SVG uses native
|
|
167
|
+
`C` commands; neither exporter flattens cubic geometry or chooses a theme.
|
|
168
|
+
The versioned JSON schema preserves all controls, subpaths, closure, dimension,
|
|
169
|
+
and caller metadata.
|
|
170
|
+
|
|
171
|
+
Matplotlib interoperability is optional:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
uv add "bezierkit[matplotlib]"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from bezierkit.adapters.matplotlib import from_path, to_path
|
|
179
|
+
|
|
180
|
+
geometry = from_path(matplotlib_path, transform=affine_transform)
|
|
181
|
+
round_trip = to_path(geometry)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Affine transforms preserve controls exactly. Non-affine transforms require
|
|
185
|
+
the explicit `approximate_path(..., tolerance=...)` API. Format guarantees and
|
|
186
|
+
round-trip tolerances are documented in `docs/exporters.md`.
|
|
187
|
+
|
|
188
|
+
## Command-line interface
|
|
189
|
+
|
|
190
|
+
Run `bezierkit` directly if it's installed in your active environment, or
|
|
191
|
+
prefix every command with `uv run` (e.g. `uv run bezierkit --help`) when
|
|
192
|
+
working inside a uv project without activating the venv.
|
|
193
|
+
|
|
194
|
+
Evaluate a curve or derivative:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
bezierkit evaluate \
|
|
198
|
+
--points "0,0" --points "1,2" --points "3,2" --points "4,0" \
|
|
199
|
+
--t 0:1:0.25 --order 1 --json
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Sample as CSV:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
bezierkit sample \
|
|
206
|
+
--points "0,0" --points "1,2" --points "3,2" --points "4,0" \
|
|
207
|
+
--count 50 --format csv > curve.csv
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Construct a curve from planar slopes and pipe its control points directly into
|
|
211
|
+
another command:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
bezierkit construct slopes \
|
|
215
|
+
--start "0,5" --end "5,0" \
|
|
216
|
+
--start-slope -2 --end-slope -0.3 \
|
|
217
|
+
| bezierkit sample --count 20 --format json
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Run `bezierkit --help` for the complete command tree. Expected domain and input
|
|
221
|
+
errors are printed without a traceback; add the global `--debug` option before
|
|
222
|
+
the subcommand when diagnosing unexpected failures.
|
|
223
|
+
|
|
224
|
+
## Scope
|
|
225
|
+
|
|
226
|
+
The 0.5 release adds stable JSON geometry, native TikZ and SVG cubic output,
|
|
227
|
+
and optional Matplotlib Path interoperability. Rendering style and diagram
|
|
228
|
+
semantics remain intentionally outside this package. Intersections, B-splines,
|
|
229
|
+
and NURBS are future work.
|