Skip to main content

Crate boxdd

Crate boxdd 

Source
Expand description

Safe, owner-scoped Rust bindings for the pinned Box2D v3 C API.

boxdd deliberately presents one ownership model and one fallible API:

  • World owns the native simulation and all Rust-side identity and callback state.
  • BodyId, ShapeId, JointId, and ChainId are copyable, world-bound identifiers intended for application storage.
  • Body, Shape, Joint, and Chain are capabilities tied to a mutable borrow of the world. Dropping one releases the borrow; destruction is always explicit.
  • Query is a read-only capability tied to its owner borrow. Reusable query buffers avoid repeated allocation on hot paths.
  • Public operations that can fail return Result. Stale IDs, invalid definitions, callback reentry, and terminal native state are reported rather than routed through a parallel panic-style API.

Live IDs cannot be detached, rebound, reconstructed from raw Box2D IDs, or used as persistence keys through Safe Rust. Use application-owned stable keys for saved state. Use boxdd_sys directly only when the caller accepts the raw FFI contract.

§Quick start

use boxdd::{BodyType, Foundation, ShapeDef, Vec2, shapes};

let foundation = Foundation::initialize_default().unwrap();
let mut world = foundation.create_world(
    foundation
        .world_builder()
        .gravity(Vec2::new(0.0, -9.8))
        .build()?,
)?;
let body_id = world.create_body(
    world
        .body_builder()
        .body_type(BodyType::Dynamic)
        .position([0.0, 2.0])
        .build()?,
)?;

{
    let mut body = world.body(body_id)?;
    body.create_polygon(
        &ShapeDef::builder().density(1.0).build()?,
        &shapes::box_polygon(0.5, 0.5)?,
    )?;
}

let completed = world.step(1.0 / 60.0, 4)?;
let contacts = completed.contact_events()?.to_owned()?;

§Ownership and identity

Creation returns a world-bound ID. Acquire a capability when an operation needs access to the corresponding object:

let body_id = world.create_body(world.body_builder().build()?)?;
world.body(body_id)?.set_awake(true)?;
world.body(body_id)?.destroy()?;

Capability acquisition validates the ID once and prevents overlapping mutable world access for the lifetime of the capability. A body, shape, joint, or chain is destroyed only by its explicit destroy method or by destruction of an owning native object/world.

ContactId values are additionally tied to a contact epoch. Inspect them through World::contact_is_valid and World::contact_data.

§Coordinates

Position and WorldTransform represent absolute world coordinates using WorldScalar, which is f32 by default and f64 with double-precision. Vec2 and Transform represent local offsets, directions, extents, and relative transforms and always remain f32.

Queries therefore take an explicit absolute origin plus local geometry:

use boxdd::{Aabb, Foundation, Position, QueryFilter, ShapeQueryBuffer};

let foundation = Foundation::initialize_default().unwrap();
let world = foundation.create_world(foundation.world_def())?;
let query = world.query()?;
let mut hits = ShapeQueryBuffer::new();
query.overlap_aabb_into(
    Position::ZERO,
    Aabb::from_center_half_extents([0.0, 0.0], [5.0, 5.0])?,
    QueryFilter::default(),
    &mut hits,
)?;

§Threading and callbacks

World and its borrow-scoped capabilities are !Send and !Sync. Keep a world on one owner thread; a dedicated physics thread plus channels is the portable integration model for multi-threaded or async applications.

On native targets, custom-filter, pre-solve, friction-mix, and restitution-mix callbacks may run on Box2D workers. Their closures are Send + Sync + 'static and receive only copyable values and branded IDs, never a world context. Query, dynamic-tree, event-view, and debug-draw callbacks are closure-scoped. Every Rust callback catches unwind-capable panics before returning through C.

WorkerCount selects the qualified built-in Box2D scheduler. Safe Rust does not expose raw task-system function pointers.

§Snapshots, recording, and replay

Snapshot is an opaque capability that can restore only its originating world. RecordingSession exclusively borrows a world and produces an opaque process-local Recording. ReplayPlayer accepts only such a recording and exposes epoch-bound views while holding exclusive process-global foundation access.

Safe Rust exposes no native snapshot or recording bytes, fresh-world snapshot loading, or replay from external bytes. Durable persistence requires an application-owned versioned schema that rebuilds a world.

§Features

  • double-precision changes absolute world coordinates and the native ABI together.
  • serde covers safe value and configuration types, not live worlds or object IDs.
  • mint, nalgebra, and glam provide scalar-correct math interop.
  • bytemuck covers layout-qualified value types.

See the repository’s migration guide and FFI lifetime audit for the complete ownership, provider, callback, and platform contracts.

Re-exports§

pub use body::Body;
pub use body::BodyBuilder;
pub use body::BodyDef;
pub use body::BodyType;
pub use body::MAX_BODY_NAME_BYTES;
pub use collision::CastOutput;
pub use collision::DistanceInput;
pub use collision::DistanceOutput;
pub use collision::LocalManifold;
pub use collision::LocalManifoldPoint;
pub use collision::MAX_LOCAL_MANIFOLD_POINTS;
pub use collision::MAX_SHAPE_PROXY_POINTS;
pub use collision::SegmentDistanceResult;
pub use collision::ShapeCastInput;
pub use collision::ShapeCastPairInput;
pub use collision::ShapeProxy;
pub use collision::SimplexCache;
pub use collision::Sweep;
pub use collision::ToiInput;
pub use collision::ToiOutput;
pub use collision::ToiState;
pub use collision::collide_capsule_and_circle;
pub use collision::collide_capsules;
pub use collision::collide_chain_segment_and_capsule;
pub use collision::collide_chain_segment_and_circle;
pub use collision::collide_chain_segment_and_polygon;
pub use collision::collide_circles;
pub use collision::collide_polygon_and_capsule;
pub use collision::collide_polygon_and_circle;
pub use collision::collide_polygons;
pub use collision::collide_segment_and_capsule;
pub use collision::collide_segment_and_circle;
pub use collision::collide_segment_and_polygon;
pub use collision::segment_distance;
pub use collision::shape_cast;
pub use collision::shape_distance;
pub use collision::time_of_impact;
pub use core::foundation::Foundation;
pub use core::foundation::FoundationActivity;
pub use core::foundation::FoundationActivityError;
pub use core::foundation::FoundationAdapterIdentityField;
pub use core::foundation::FoundationConfig;
pub use core::foundation::FoundationDiagnostics;
pub use core::foundation::FoundationInitError;
pub use core::foundation::FoundationAssertHook;
pub use core::foundation::FoundationLogHook;
pub use core::math::HASH_INIT;
pub use core::math::Rot;
pub use core::math::Transform;
pub use core::math::Version;
pub use core::math::allocated_byte_count;
pub use core::math::atan2;
pub use core::math::compute_cos_sin;
pub use core::math::hash_bytes;
pub use core::math::is_valid_float;
pub use core::math::milliseconds_and_reset;
pub use core::math::milliseconds_since;
pub use core::math::rotation_between_unit_vectors;
pub use core::math::ticks;
pub use core::math::version;
pub use core::math::yield_now;
pub use debug_draw::DebugDraw;
pub use debug_draw::DebugDrawCmd;
pub use debug_draw::DebugDrawOptions;
pub use debug_draw::HexColor;
pub use dynamic_tree::DynamicTree;
pub use dynamic_tree::TreeBoxCastInput;
pub use dynamic_tree::TreeCastControl;
pub use dynamic_tree::TreeProxyId;
pub use dynamic_tree::TreeRayCastInput;
pub use dynamic_tree::TreeStats;
pub use error::Error;
pub use error::Result;
pub use events::BodyEvents;
pub use events::BodyMoveEvent;
pub use events::CompletedStep;
pub use events::ContactBeginTouchEvent;
pub use events::ContactEndTouchEvent;
pub use events::ContactEvents;
pub use events::ContactEventsView;
pub use events::ContactHitEvent;
pub use events::JointEvent;
pub use events::JointEvents;
pub use events::SensorBeginTouchEvent;
pub use events::SensorEndTouchEvent;
pub use events::SensorEvents;
pub use events::SensorEventsView;
pub use events::StepEventsSnapshot;
pub use filter::Filter;
pub use joints::ConstraintTuning;
pub use joints::DistanceJoint;
pub use joints::DistanceJointBuilder;
pub use joints::DistanceJointDef;
pub use joints::FilterJoint;
pub use joints::FilterJointBuilder;
pub use joints::FilterJointDef;
pub use joints::Joint;
pub use joints::JointBase;
pub use joints::JointType;
pub use joints::MotorJoint;
pub use joints::MotorJointBuilder;
pub use joints::MotorJointDef;
pub use joints::PrismaticJoint;
pub use joints::PrismaticJointBuilder;
pub use joints::PrismaticJointDef;
pub use joints::RevoluteJoint;
pub use joints::RevoluteJointBuilder;
pub use joints::RevoluteJointDef;
pub use joints::WeldJoint;
pub use joints::WeldJointBuilder;
pub use joints::WeldJointDef;
pub use joints::WheelJoint;
pub use joints::WheelJointBuilder;
pub use joints::WheelJointDef;
pub use query::Aabb;
pub use query::ClosestRayCastResult;
pub use query::CollisionPlane;
pub use query::MoverPlaneResult;
pub use query::Plane;
pub use query::PlaneSolverResult;
pub use query::Query;
pub use query::QueryFilter;
pub use query::RayResult;
pub use query::clip_vector;
pub use query::solve_planes;
pub use query::MoverQueryBuffer;
pub use query::RayQueryBuffer;
pub use query::ShapeQueryBuffer;
pub use recording::MixerId;
pub use recording::MixerIdentities;
pub use recording::Recording;
pub use recording::RecordingLimits;
pub use recording::RecordingSession;
pub use replay::ReplayBodyView;
pub use replay::ReplayConfig;
pub use replay::ReplayEpoch;
pub use replay::ReplayInfo;
pub use replay::ReplayKeyframePolicy;
pub use replay::ReplayKeyframeState;
pub use replay::ReplayPlayer;
pub use replay::ReplayQueryHitView;
pub use replay::ReplayQueryKind;
pub use replay::ReplayQueryView;
pub use replay::ReplayStatus;
pub use replay::ReplayView;
pub use shapes::chain::Chain;
pub use shapes::chain::ChainDef;
pub use shapes::chain::ChainDefBuilder;
pub use shapes::chain::ChainDefMaterialLayout;
pub use shapes::Capsule;
pub use shapes::ChainSegment;
pub use shapes::Circle;
pub use shapes::MAX_POLYGON_VERTICES;
pub use shapes::Polygon;
pub use shapes::Segment;
pub use shapes::Shape;
pub use shapes::ShapeDef;
pub use shapes::ShapeDefBuilder;
pub use shapes::ShapeType;
pub use shapes::SurfaceMaterial;
pub use snapshot::PreparedSnapshotRestore;
pub use snapshot::Snapshot;
pub use snapshot::SnapshotRestore;
pub use types::BodyId;
pub use types::ChainId;
pub use types::ContactData;
pub use types::ContactId;
pub use types::JointId;
pub use types::MAX_MANIFOLD_POINTS;
pub use types::Manifold;
pub use types::ManifoldPoint;
pub use types::MassData;
pub use types::MotionLocks;
pub use types::Position;
pub use types::PositionToLocalError;
pub use types::ShapeId;
pub use types::Vec2;
pub use types::WorldCastOutput;
pub use types::WorldScalar;
pub use types::WorldTransform;
pub use types::WorldTransformFromInteropError;
pub use world::B2_MAX_WORKERS;
pub use world::Counters;
pub use world::MaterialMixInput;
pub use world::Profile;
pub use world::WorkerCount;
pub use world::World;
pub use world::WorldBuilder;
pub use world::WorldCapacity;
pub use world::WorldDef;
pub use world_extras::ExplosionDef;

Modules§

body
collision
Standalone low-level collision geometry helpers.
contact
core
debug_draw
Debug Draw bridge to Box2D v3 callbacks.
dynamic_tree
Safe wrapper for Box2D’s standalone dynamic AABB tree.
error
Common errors for the safe API.
events
Lazy, borrow-scoped event access for one completed simulation step.
filter
id
Opaque world-bound Box2D object identifiers.
joints
Joint definitions, creation helpers, and borrow-scoped runtime capabilities.
prelude
query
Borrow-scoped broad-phase queries, casts, and character-mover helpers.
recording
Owned Box2D recording sessions and opaque process-local recordings.
replay
Owned, preflighted Box2D recording playback.
shapes
Shapes API
snapshot
Transactional Box2D world snapshots.
tuning
Tuning Notes and Upstream Constants
types
world
world_extras
Additional world runtime helpers and value types that sit beside the core world API.