Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions .github/workflows/python-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ on:
env:
CARGO_TERM_COLOR: always

# The four engine packages live under python/rapier-py-{2,3}d{,-f64}; the
# pure-Python testbed under python/rapier-testbed; the shared parity test
# suite under python/tests (imports all four engine packages at once).
# The engine package lives under python/rapier-py-3d (rapier3d, f32); the
# pure-Python testbed under python/rapier-testbed; the test suite under
# python/tests.
jobs:
lint-rust:
runs-on: ubuntu-latest
Expand All @@ -32,9 +32,7 @@ jobs:
- name: Cargo clippy (Python bindings)
run: |
cargo clippy --no-deps \
-p rapier-py-core \
-p rapier-py-2d -p rapier-py-2d-f64 \
-p rapier-py-3d -p rapier-py-3d-f64 \
-p rapier-py-3d \
-- -D warnings

test:
Expand Down Expand Up @@ -64,18 +62,15 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install maturin pytest pytest-timeout hypothesis numpy matplotlib
# Build + install all four engine packages into the runner's environment
# so the cross-flavor parity suite can import every variant.
- name: Build & install engine packages
# Build + install the rapier3d (f32) engine package into the runner's
# environment so the test suite can import it.
- name: Build & install engine package
shell: bash
run: |
for crate in rapier-py-2d rapier-py-2d-f64 rapier-py-3d rapier-py-3d-f64; do
python -m pip install --no-build-isolation "./python/$crate"
done
- name: Install testbed (no deps; engine packages already present)
run: python -m pip install --no-build-isolation ./python/rapier-py-3d
- name: Install testbed (no deps; engine package already present)
shell: bash
run: python -m pip install --no-deps ./python/rapier-testbed
- name: Smoke (import surface)
run: python -m pytest python/tests/test_smoke.py python/tests/test_math.py python/tests/test_math_2d.py -v
- name: Full parity test suite
run: python -m pytest python/tests/test_smoke.py python/tests/test_math.py -v
- name: Full test suite
run: python -m pytest python/tests/ -q --timeout=120
13 changes: 5 additions & 8 deletions .github/workflows/python-wheels.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: Python wheels

# Build abi3 wheels for the four engine packages across every supported
# platform and (on a `python-v*` tag) publish them to PyPI via trusted
# publishing.
# Build abi3 wheels for the rapier3d (f32) engine package across every
# supported platform and (on a `python-v*` tag) publish them to PyPI via
# trusted publishing.
#
# abi3-py39 means ONE wheel per (package, platform, arch) covers Python 3.9+,
# so there is deliberately no Python-version axis.
#
# NOTE: no sdist is published. The rapier2d/3d engine crates share their
# NOTE: no sdist is published. The rapier3d engine crate shares its
# source via the repo-root `src/` tree (`[lib] path = "../../src/lib.rs"`),
# which lives outside any vendored crate directory, and maturin's sdist
# packer cannot reach parent paths (`..` is rejected in `include`). A source
Expand Down Expand Up @@ -38,9 +38,6 @@ jobs:
matrix:
package:
- { dir: rapier-py-3d, name: rapier3d }
- { dir: rapier-py-3d-f64, name: rapier3d-f64 }
- { dir: rapier-py-2d, name: rapier2d }
- { dir: rapier-py-2d-f64, name: rapier2d-f64 }
platform:
- { runner: ubuntu-latest, target: x86_64-unknown-linux-gnu, manylinux: auto }
- { runner: ubuntu-latest, target: aarch64-unknown-linux-gnu, manylinux: auto }
Expand Down Expand Up @@ -77,7 +74,7 @@ jobs:
path: dist
pattern: "wheels-*"
merge-multiple: true
- name: Publish all four packages
- name: Publish the rapier3d package
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ members = [
"crates/rapier3d-meshloader",
"crates/mjcf-rs",
"crates/rapier3d-mjcf",
"python/rapier-py-core",
"python/rapier-py-2d",
"python/rapier-py-2d-f64",
"python/rapier-py-3d",
"python/rapier-py-3d-f64",
]
# Bare `cargo build` / `cargo test` / `cargo clippy` only touches the
# pure-Rust crates. The Python-binding crates need a Python build env
Expand Down
70 changes: 26 additions & 44 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
# Rapier Python bindings

The Rapier physics engine exposed to Python via [PyO3](https://pyo3.rs/) and
[maturin](https://www.maturin.rs/). The bindings ship as **four independent
PyPI packages**, one per `(dimension, scalar)` flavor — they share an
identical API differing only in dimension and scalar type:
[maturin](https://www.maturin.rs/). The bindings ship as a single PyPI
package wrapping the 3D, `f32` engine:

| PyPI dist | import name | crate (`python/…`) | dim | scalar |
| ------------- | -------------- | ------------------ | --- | ------ |
| `rapier2d` | `rapier2d` | `rapier-py-2d` | 2D | f32 |
| `rapier3d` | `rapier3d` | `rapier-py-3d` | 3D | f32 |
| `rapier2d-f64`| `rapier2d_f64` | `rapier-py-2d-f64` | 2D | f64 |
| `rapier3d-f64`| `rapier3d_f64` | `rapier-py-3d-f64` | 3D | f64 |

`rapier-py-core` holds the shared PyO3 binding macros (compiled into each
`rapier-py-core` holds the shared PyO3 binding macros (compiled into the
package; it is not published on its own). The Panda3D visual testbed lives in
the separate `rapier-testbed` package.

Full documentation lives in [`docs/`](docs/); the design of this split is
recorded in [`docs/repackaging/`](docs/repackaging/).
Full documentation lives in [`docs/`](docs/).

> **Note:** these are not yet published on PyPI. The instructions below build
> the packages locally from this checkout. There is currently no source
> **Note:** this is not yet published on PyPI. The instructions below build
> the package locally from this checkout. There is currently no source
> distribution (sdist), so building requires the full git repository plus a
> Rust toolchain — see [`docs/repackaging/05-publishing-pypi.md`](docs/repackaging/05-publishing-pypi.md).
> Rust toolchain.

## Prerequisites

Expand All @@ -39,13 +34,13 @@ pip install maturin

## Quick start

[`python/dev.sh`](dev.sh) does everything below in one command — builds all four
packages, runs the test suite, builds the docs, and smoke-tests the testbed. It
[`python/dev.sh`](dev.sh) does everything below in one command — builds the
package, runs the test suite, builds the docs, and smoke-tests the testbed. It
creates and manages a `.venv` at the repo root on first run:

```bash
python/dev.sh # build + test + docs + testbed (headless smoke)
python/dev.sh build # just build the four packages
python/dev.sh build # just build the package
python/dev.sh test # build + run the test suite
python/dev.sh docs # build + build the docs
python/dev.sh testbed # build + install testbed, open the picker
Expand All @@ -56,39 +51,29 @@ The sections below are the manual equivalents.

## 1. Build the bindings

Each flavor is its own package. Build the one(s) you want, editable, with
`maturin develop` (add `--release` for an optimized build — slower to compile,
much faster at runtime):
Build the package, editable, with `maturin develop` (add `--release` for an
optimized build — slower to compile, much faster at runtime):

```bash
maturin develop -m python/rapier-py-3d/Cargo.toml # 3D f32 -> import rapier3d
```

Swap `rapier-py-3d` for `rapier-py-2d`, `rapier-py-3d-f64`, or
`rapier-py-2d-f64` to build the other flavors (see the table above for the
matching import name).

```bash
python -c "import rapier3d; print(rapier3d.__version__)" # smoke check
```

### Run the test suite

The test suite is cross-flavor (it parametrizes over all four packages), so
install every flavor first:

```bash
for c in rapier-py-2d rapier-py-2d-f64 rapier-py-3d rapier-py-3d-f64; do
maturin develop --release -m "python/$c/Cargo.toml"
done
maturin develop --release -m python/rapier-py-3d/Cargo.toml
pip install pytest pytest-timeout hypothesis numpy matplotlib
python -m pytest python/tests # ~708 passed
python -m pytest python/tests
```

## 2. Build and open the docs

The docs use Sphinx autodoc, so the engine packages must be **built first**
(step 1 — build all four to avoid import warnings). Then:
The docs use Sphinx autodoc, so the engine package must be **built first**
(step 1). Then:

```bash
pip install sphinx furo sphinx-autodoc-typehints
Expand All @@ -100,14 +85,13 @@ open _build/html/index.html # macOS; Linux: xdg-open; Windows: start
## 3. Run the testbed examples

`rapier-testbed` is a [Panda3D](https://www.panda3d.org/) visual gallery of
~127 examples ported from the Rust `examples2d/` and `examples3d/`. It drives
both the 2D and 3D engines.
examples ported from the Rust `examples3d/`. It drives the 3D engine.

### Install

The testbed depends on `rapier2d` and `rapier3d`. Since those aren't on PyPI
yet, build them first (step 1 above), then install the testbed with
`--no-deps` so pip uses your local builds instead of trying to fetch them.
The testbed depends on `rapier3d`. Since it isn't on PyPI yet, build it first
(step 1 above), then install the testbed with `--no-deps` so pip uses your
local build instead of trying to fetch it.
Run this **from the repository root** (the `./python/...` path is relative to
it, like the build steps above):

Expand All @@ -130,10 +114,8 @@ Once installed, these run from **any** directory (they invoke the installed
# Interactive picker — browse every example by category (opens a window):
python -m rapier_testbed

# Jump straight into one example. 3D examples live under `examples3`,
# 2D under `examples2`:
# Jump straight into one example. 3D examples live under `examples3`:
python -m rapier_testbed.examples3.domino3
python -m rapier_testbed.examples2.pyramid2

# Headless — run a fixed number of steps with no window (needs no display;
# this is what CI uses):
Expand All @@ -144,7 +126,7 @@ To walk through **every** example in turn — each opens in a window, and
closing it launches the next (Ctrl-C to stop):

```bash
python python/examples_tour.py # all (2D then 3D); accepts 2d|3d|--start NAME
python python/examples_tour.py # every 3D example; accepts --start NAME
python/dev.sh tour # same, but builds + installs the testbed first
```

Expand All @@ -158,10 +140,10 @@ python/dev.sh tour # same, but builds + installs the testbe
| | | | `W` | toggle wireframe |
| | | | `Esc` | quit |

The examples span 9 categories: Collisions, Dynamics, Joints, Controls,
Robotics, Stress Tests, Debug, Misc, and "Inspired by Solver 2D". The picker
lists them all; each example's module name (for direct launch) matches its
file under `python/rapier-testbed/rapier_testbed/examples{2,3}/`.
The examples span categories such as Collisions, Dynamics, Joints, Controls,
Robotics, Stress Tests, Debug, and Misc. The picker lists them all; each
example's module name (for direct launch) matches its file under
`python/rapier-testbed/rapier_testbed/examples3/`.

## License

Expand Down
13 changes: 6 additions & 7 deletions python/dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
#
# Usage (run from anywhere; paths are resolved relative to the repo):
# python/dev.sh # all: build + test + docs + testbed (headless smoke)
# python/dev.sh build # build all four engine packages (editable)
# python/dev.sh build # build the rapier3d engine package (editable)
# python/dev.sh test # build, then run the full test suite
# python/dev.sh docs # build, then build the HTML docs
# python/dev.sh testbed # build + install testbed, open the picker
# python/dev.sh testbed examples3.domino3 # build + install testbed, run one example
# python/dev.sh tour # build + install testbed, run every example in turn
# # (close a window to advance; accepts 2d|3d|--start NAME)
# # (close a window to advance; accepts --start NAME)
# python/dev.sh clean # remove build artifacts + the managed venv
#
# Environment:
Expand All @@ -26,7 +26,7 @@ cd "$REPO_ROOT"

PROFILE="${PROFILE:-release}"
VENV="${RAPIER_PY_VENV:-$REPO_ROOT/.venv}"
CRATES=(rapier-py-2d rapier-py-2d-f64 rapier-py-3d rapier-py-3d-f64)
CRATES=(rapier-py-3d)

log() { printf '\n\033[1;34m==>\033[0m \033[1m%s\033[0m\n' "$*"; }
note() { printf ' %s\n' "$*"; }
Expand Down Expand Up @@ -65,8 +65,7 @@ build_all() {
log "Building $c ($PROFILE)"
maturin_develop "python/$c/Cargo.toml"
done
python -c "import rapier2d, rapier2d_f64, rapier3d, rapier3d_f64 as _; \
print('built all four flavors, version', rapier3d.__version__)"
python -c "import rapier3d as _; print('built rapier3d, version', _.__version__)"
}

run_tests() {
Expand All @@ -86,7 +85,7 @@ install_testbed() {
pip_install panda3d numpy
# Editable (-e) so edits to examples / camera / the testbed itself are
# picked up by `python -m rapier_testbed` without reinstalling. --no-deps
# because its rapier2d/rapier3d deps are the local builds from build_all.
# because its rapier3d dep is the local build from build_all.
# Note: --no-deps must precede -e, else pip reads it as the -e target.
pip_install --no-deps -e "$REPO_ROOT/python/rapier-testbed"
}
Expand All @@ -111,7 +110,7 @@ cmd_testbed() {

cmd_tour() {
# Launch every example one after another (close a window to advance).
# Extra args (2d|3d|--start NAME) pass through to examples_tour.py.
# Extra args (--start NAME) pass through to examples_tour.py.
ensure_venv; build_all; install_testbed
log "Touring all examples (close each window to advance; Ctrl-C to stop)"
python "$REPO_ROOT/python/examples_tour.py" "$@"
Expand Down
9 changes: 1 addition & 8 deletions python/docs/api/errors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ Every exception the bindings raise derives from a single base,
rapier-specific failure; catch the leaf types when you need to tell them
apart.

Each ``(dim, scalar)`` package has its **own** error tree — there is no
cross-flavor base shared between, say, ``rapier3d`` and ``rapier2d``, so
catch the error type from the package you imported.

Base
----

Expand All @@ -32,10 +28,7 @@ Core errors
Loader errors
-------------

Raised by the loaders (see :doc:`loaders`). The error types are exported
by every package for a uniform error tree, but only the f32-3D
``rapier3d`` package ships loaders that actually raise them — the 2D and
f64 packages expose no loader machinery.
Raised by the loaders (see :doc:`loaders`).

.. autoexception:: MeshConversionError
:show-inheritance:
Expand Down
16 changes: 0 additions & 16 deletions python/docs/api/geometry.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,6 @@ Shapes
.. autoclass:: Cone
.. autoclass:: ConvexPolyhedron

``Cylinder``, ``Cone``, and ``ConvexPolyhedron`` are 3D-only. The 2D
packages instead expose the flat shapes below.

2D-only shapes
~~~~~~~~~~~~~~

The same surface in 2D lives under :mod:`rapier2d`:

.. currentmodule:: rapier2d

.. autoclass:: Segment
.. autoclass:: Polyline
.. autoclass:: ConvexPolygon

.. currentmodule:: rapier3d

Bounding volumes
----------------

Expand Down
6 changes: 2 additions & 4 deletions python/docs/api/loaders.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
Loaders
=======

Loaders for external scene formats. All three are **3D-only / f32**:
the upstream ``rapier3d-urdf``, ``rapier3d-meshloader``, and
``rapier3d-mjcf`` crates are 3D-only and have no ``-f64`` variant, so
the loaders are registered solely by the f32-3D ``rapier3d`` cdylib.
Loaders for external scene formats, backed by the upstream
``rapier3d-urdf``, ``rapier3d-meshloader``, and ``rapier3d-mjcf`` crates.

URDF
----
Expand Down
Loading
Loading