boxdd/lib.rs
1#![allow(rustdoc::broken_intra_doc_links)]
2//! boxdd: Safe, ergonomic Rust bindings for Box2D (v3 C API)
3//!
4//! Highlights
5//! - Thin safe layer on top of the official Box2D v3 C API.
6//! - Modular API: world, bodies, shapes, joints, queries, events, debug draw.
7//! - Ergonomics: builder patterns, world-space helpers, optional `mint` integration.
8//! - Three usage styles:
9//! - Owned handles: `OwnedBody`/`OwnedShape`/`OwnedJoint`/`OwnedChain` (Drop destroys; easy to store).
10//! - Scoped handles: `Body<'_>`/`Shape<'_>`/`Joint<'_>`/`Chain<'_>` (dropping only releases the world borrow).
11//! - ID-style: raw ids (`BodyId`/`ShapeId`/`JointId`/`ChainId`) for maximum flexibility.
12//! - Safe handle methods validate ids and panic on invalid ids (prevents UB if an id becomes stale).
13//! For recoverable failures (invalid ids / calling during Box2D callbacks), use `try_*` APIs returning `ApiResult<T>`.
14//! - Threading: `World` and owned handles are `!Send`/`!Sync`. Run physics on one thread; in async runtimes prefer
15//! `spawn_local`/`LocalSet`, or create the world inside a dedicated physics thread and communicate via channels.
16//!
17//! Quickstart (owned handles)
18//! ```no_run
19//! use boxdd::{World, WorldDef, BodyBuilder, ShapeDef, shapes, Vec2};
20//! let def = WorldDef::builder().gravity(Vec2::new(0.0, -9.8)).build();
21//! let mut world = World::new(def).unwrap();
22//! let body = world.create_body_owned(BodyBuilder::new().position([0.0, 2.0]).build());
23//! let sdef = ShapeDef::builder().density(1.0).build();
24//! let poly = shapes::box_polygon(0.5, 0.5);
25//! let _shape = world.create_polygon_shape_for_owned(body.id(), &sdef, &poly);
26//! world.step(1.0/60.0, 4);
27//! ```
28//!
29//! Quickstart (scoped handles)
30//! ```no_run
31//! use boxdd::{World, WorldDef, BodyBuilder, ShapeDef, shapes, Vec2};
32//! let def = WorldDef::builder().gravity(Vec2::new(0.0, -9.8)).build();
33//! let mut world = World::new(def).unwrap();
34//! {
35//! // Limit the borrow of `world` by scoping the body handle.
36//! let mut body = world.create_body(BodyBuilder::new().position([0.0, 2.0]).build());
37//! let sdef = ShapeDef::builder().density(1.0).build();
38//! let poly = shapes::box_polygon(0.5, 0.5);
39//! let _shape = body.create_polygon_shape(&sdef, &poly);
40//! }
41//! world.step(1.0/60.0, 4);
42//! ```
43//!
44//! Quickstart (ID-style)
45//! ```no_run
46//! use boxdd::{World, WorldDef, BodyBuilder, ShapeDef, shapes, Vec2};
47//! let def = WorldDef::builder().gravity(Vec2::new(0.0, -9.8)).build();
48//! let mut world = World::new(def).unwrap();
49//! let body_id = world.create_body_id(BodyBuilder::new().position([0.0, 2.0]).build());
50//! let sdef = ShapeDef::builder().density(1.0).build();
51//! let poly = shapes::box_polygon(0.5, 0.5);
52//! let _shape_id = world.create_polygon_shape_for(body_id, &sdef, &poly);
53//! world.step(1.0/60.0, 4);
54//! ```
55//!
56//! Math interop (optional features)
57//! - `Vec2` always accepts `[f32; 2]` and `(f32, f32)` anywhere `Into<Vec2>` is used.
58//! - With `mint`, `cgmath`, `nalgebra`, or `glam` enabled, `Vec2` also accepts those crates'
59//! 2D vector/point types via `From`/`Into`.
60//! - Returned vectors can be converted back using `From` to the corresponding math types.
61//!
62//! Modules
63//! - `world`, `body`, `shapes`, `joints`, `query`, `events`, `debug_draw`, `prelude`.
64//! Import `boxdd::prelude::*` for the most common types.
65//!
66//! Queries (AABB + Ray Cast)
67//! ```no_run
68//! use boxdd::{World, WorldDef, BodyBuilder, ShapeDef, shapes, Vec2, Aabb, QueryFilter};
69//! let mut world = World::new(WorldDef::builder().gravity([0.0,-9.8]).build()).unwrap();
70//! let b = world.create_body_id(BodyBuilder::new().position([0.0, 2.0]).build());
71//! let sdef = ShapeDef::builder().density(1.0).build();
72//! world.create_polygon_shape_for(b, &sdef, &shapes::box_polygon(0.5, 0.5));
73//! // AABB overlap
74//! let hits = world.overlap_aabb(Aabb::from_center_half_extents([0.0, 1.0], [1.0, 1.5]), QueryFilter::default());
75//! assert!(!hits.is_empty());
76//! // Ray (closest)
77//! let r = world.cast_ray_closest(Vec2::new(0.0, 5.0), Vec2::new(0.0, -10.0), QueryFilter::default());
78//! if r.hit { let _ = (r.point, r.normal, r.fraction); }
79//! ```
80//!
81//! Feature Flags
82//! - `serialize`: scene snapshot helpers (save/apply world config; build/restore minimal full-scene snapshot).
83//! - `pkg-config`: allow linking against a system `box2d` via pkg-config.
84//! - `mint`: lightweight math interop types (`mint::Vector2`, `mint::Point2`, and 2D affine matrices for `Transform`).
85//! - `cgmath` / `nalgebra` / `glam`: conversions with their 2D math types.
86//! - `bytemuck`: `Pod`/`Zeroable` for core math types (`Vec2`, `Rot`, `Transform`, `Aabb`) for zero-copy interop.
87//!
88//! Events
89//! - Three access styles:
90//! - By value: `world.contact_events()`/`sensor_events()`/`body_events()`/`joint_events()` return owned data for storage or cross‑frame use.
91//! - Zero‑copy views: `with_*_events_view(...)` iterate without allocations (borrows internal buffers).
92//! - Raw slices: `unsafe { with_*_events(...) }` expose FFI slices (borrows internal buffers).
93//!
94//! Example (zero‑copy views)
95//! ```no_run
96//! use boxdd::prelude::*;
97//! let mut world = World::new(WorldDef::default()).unwrap();
98//! world.with_contact_events_view(|begin, end, hit| {
99//! let _ = (begin.count(), end.count(), hit.count());
100//! });
101//! world.with_sensor_events_view(|beg, end| { let _ = (beg.count(), end.count()); });
102//! world.with_body_events_view(|moves| { for m in moves { let _ = (m.body_id(), m.fell_asleep()); } });
103//! world.with_joint_events_view(|j| { let _ = j.count(); });
104//! ```
105
106pub mod body;
107pub mod debug_draw;
108pub mod error;
109pub mod events;
110pub mod filter;
111pub mod joints;
112pub mod prelude;
113pub mod query;
114#[cfg(feature = "serialize")]
115#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
116pub mod serialize;
117pub mod shapes;
118pub mod tuning;
119pub mod types;
120#[cfg(feature = "unchecked")]
121#[cfg_attr(docsrs, doc(cfg(feature = "unchecked")))]
122pub mod unchecked;
123pub mod world;
124pub mod world_extras;
125pub mod core {
126 pub(crate) mod box2d_lock;
127 pub(crate) mod callback_state;
128 pub(crate) mod debug_checks;
129 pub mod math;
130 #[cfg(feature = "serialize")]
131 pub(crate) mod serialize_registry;
132 pub(crate) mod user_data;
133 pub(crate) mod world_core;
134}
135
136pub use body::OwnedBody;
137pub use body::{Body, BodyBuilder, BodyDef, BodyType};
138#[cfg(feature = "cgmath")]
139#[cfg_attr(docsrs, doc(cfg(feature = "cgmath")))]
140pub use core::math::TransformFromCgmathError;
141#[cfg(feature = "glam")]
142#[cfg_attr(docsrs, doc(cfg(feature = "glam")))]
143pub use core::math::TransformFromGlamError;
144#[cfg(feature = "mint")]
145#[cfg_attr(docsrs, doc(cfg(feature = "mint")))]
146pub use core::math::TransformFromMintError;
147pub use core::math::{Rot, Transform};
148pub use debug_draw::{DebugDraw, DebugDrawCmd, DebugDrawOptions};
149pub use error::{ApiError, ApiResult};
150pub use events::{
151 BodyMoveEvent, ContactBeginTouchEvent, ContactEndTouchEvent, ContactEvents, ContactHitEvent,
152 JointEvent, SensorBeginTouchEvent, SensorEndTouchEvent, SensorEvents,
153};
154pub use filter::Filter;
155pub use joints::{
156 DistanceJointBuilder, DistanceJointDef, FilterJointBuilder, FilterJointDef, Joint, JointBase,
157 JointBaseBuilder, MotorJointBuilder, MotorJointDef, PrismaticJointBuilder, PrismaticJointDef,
158 RevoluteJointBuilder, RevoluteJointDef, WeldJointBuilder, WeldJointDef, WheelJointBuilder,
159 WheelJointDef,
160};
161pub use query::{Aabb, QueryFilter, RayResult};
162pub use shapes::chain::{Chain, ChainDef, ChainDefBuilder, OwnedChain};
163pub use shapes::{OwnedShape, Shape, ShapeDef, ShapeDefBuilder, SurfaceMaterial};
164pub use types::Vec2;
165pub use world::{
166 CallbackWorld, OutstandingOwnedHandles, OwnedHandleCounts, World, WorldBuilder, WorldDef,
167 WorldHandle,
168};