use crate::Transform;
use crate::body::{Body, BodyDef, BodyType};
use crate::collision::CastOutput;
use crate::core::world_core::{CustomFilterCtx, MaterialMixCtx, PreSolveCtx, WorldCore};
use crate::query::Aabb;
use crate::shapes::{ShapeDef, SurfaceMaterial};
use crate::types::{BodyId, ChainId, JointId, MassData, MotionLocks, ShapeId, Vec2};
use boxdd_sys::ffi;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::Arc;
mod body_api;
mod borrow;
mod creation;
mod definition;
mod handle;
mod metrics;
mod runtime;
mod shape_api;
pub use definition::{Error, WorldBuilder, WorldDef};
pub(crate) use definition::{
assert_non_negative_finite_world_scalar, assert_positive_finite_world_scalar,
assert_world_gravity_valid, check_non_negative_finite_world_scalar,
check_positive_finite_world_scalar, check_world_gravity_valid,
};
pub use handle::{CallbackWorld, WorldHandle};
pub use metrics::{Counters, OutstandingOwnedHandles, OwnedHandleCounts, Profile};
pub use runtime::MaterialMixInput;
pub(crate) use runtime::{
try_world_awake_body_count_impl, try_world_counters_impl, try_world_gravity_impl,
try_world_hit_event_threshold_impl, try_world_is_continuous_enabled_impl,
try_world_is_sleeping_enabled_impl, try_world_is_warm_starting_enabled_impl,
try_world_maximum_linear_speed_impl, try_world_profile_impl,
try_world_restitution_threshold_impl, world_awake_body_count_checked_impl,
world_counters_checked_impl, world_gravity_checked_impl,
world_hit_event_threshold_checked_impl, world_is_continuous_enabled_checked_impl,
world_is_sleeping_enabled_checked_impl, world_is_warm_starting_enabled_checked_impl,
world_maximum_linear_speed_checked_impl, world_profile_checked_impl,
world_restitution_threshold_checked_impl,
};
#[inline]
fn raw_body_id(id: BodyId) -> ffi::b2BodyId {
id.into_raw()
}
#[inline]
fn raw_shape_id(id: ShapeId) -> ffi::b2ShapeId {
id.into_raw()
}
#[inline]
fn raw_joint_id(id: JointId) -> ffi::b2JointId {
id.into_raw()
}
#[inline]
fn raw_chain_id(id: ChainId) -> ffi::b2ChainId {
id.into_raw()
}
pub struct World {
core: Arc<WorldCore>,
_not_send_sync: core::marker::PhantomData<Rc<()>>,
}
#[cfg(feature = "serialize")]
pub use crate::core::serialize_registry::{
ChainCreateRecord, ChainMaterialsRecord, ShapeFlagsRecord,
};
impl World {
pub fn new(def: WorldDef) -> Result<Self, Error> {
def.validate()?;
let _guard = crate::core::box2d_lock::lock();
let raw = def.into_raw();
let world_id = unsafe { ffi::b2CreateWorld(&raw) };
let ok = unsafe { ffi::b2World_IsValid(world_id) };
if ok {
Ok(Self {
core: WorldCore::new(world_id),
_not_send_sync: core::marker::PhantomData,
})
} else {
Err(Error::CreateFailed)
}
}
pub fn world_id_raw(&self) -> ffi::b2WorldId {
self.core.id
}
pub(crate) fn raw(&self) -> ffi::b2WorldId {
self.world_id_raw()
}
pub(crate) fn core_arc(&self) -> Arc<WorldCore> {
Arc::clone(&self.core)
}
pub(crate) fn with_borrowed_event_buffers<T>(&self, f: impl FnOnce() -> T) -> T {
crate::core::callback_state::assert_not_in_callback();
let core = self.core_arc();
let out = {
let _borrow = core.borrow_event_buffers();
f()
};
core.process_deferred_destroys();
out
}
pub(crate) fn try_with_borrowed_event_buffers<T>(
&self,
f: impl FnOnce() -> T,
) -> crate::error::ApiResult<T> {
crate::core::callback_state::check_not_in_callback()?;
Ok(self.with_borrowed_event_buffers(f))
}
pub fn set_user_data<T: 'static>(&mut self, value: T) {
crate::core::callback_state::assert_not_in_callback();
let p = self.core.set_world_user_data(value);
unsafe { ffi::b2World_SetUserData(self.raw(), p) };
}
pub fn try_set_user_data<T: 'static>(&mut self, value: T) -> crate::error::ApiResult<()> {
crate::core::callback_state::check_not_in_callback()?;
let p = self.core.set_world_user_data(value);
unsafe { ffi::b2World_SetUserData(self.raw(), p) };
Ok(())
}
pub fn clear_user_data(&mut self) -> bool {
crate::core::callback_state::assert_not_in_callback();
let had = unsafe { !ffi::b2World_GetUserData(self.raw()).is_null() };
unsafe { ffi::b2World_SetUserData(self.raw(), core::ptr::null_mut()) };
self.core.clear_world_user_data();
had
}
pub fn try_clear_user_data(&mut self) -> crate::error::ApiResult<bool> {
crate::core::callback_state::check_not_in_callback()?;
let had = unsafe { !ffi::b2World_GetUserData(self.raw()).is_null() };
unsafe { ffi::b2World_SetUserData(self.raw(), core::ptr::null_mut()) };
self.core.clear_world_user_data();
Ok(had)
}
pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
crate::core::callback_state::assert_not_in_callback();
self.core
.try_with_world_user_data(f)
.expect("user data type mismatch")
}
pub fn try_with_user_data<T: 'static, R>(
&self,
f: impl FnOnce(&T) -> R,
) -> crate::error::ApiResult<Option<R>> {
crate::core::callback_state::check_not_in_callback()?;
self.core.try_with_world_user_data(f)
}
pub fn take_user_data<T: 'static>(&mut self) -> Option<T> {
crate::core::callback_state::assert_not_in_callback();
let v = self
.core
.take_world_user_data::<T>()
.expect("user data type mismatch");
if v.is_some() {
unsafe { ffi::b2World_SetUserData(self.raw(), core::ptr::null_mut()) };
}
v
}
pub fn try_take_user_data<T: 'static>(&mut self) -> crate::error::ApiResult<Option<T>> {
crate::core::callback_state::check_not_in_callback()?;
let v = self.core.take_world_user_data::<T>()?;
if v.is_some() {
unsafe { ffi::b2World_SetUserData(self.raw(), core::ptr::null_mut()) };
}
Ok(v)
}
pub fn handle(&self) -> WorldHandle {
WorldHandle::new(Arc::clone(&self.core))
}
pub fn owned_handle_count(&self) -> usize {
Arc::strong_count(&self.core).saturating_sub(1)
}
pub fn owned_handle_counts(&self) -> OwnedHandleCounts {
let (bodies, shapes, joints, chains) = self.core.owned_counts();
OwnedHandleCounts {
bodies,
shapes,
joints,
chains,
}
}
pub fn shutdown(self) -> Result<(), (Self, OutstandingOwnedHandles)> {
let strong = Arc::strong_count(&self.core);
if strong == 1 {
Ok(())
} else {
let (bodies, shapes, joints, chains) = self.core.owned_counts();
Err((
self,
OutstandingOwnedHandles {
strong_count: strong,
counts: OwnedHandleCounts {
bodies,
shapes,
joints,
chains,
},
},
))
}
}
#[cfg(feature = "serialize")]
pub fn body_ids(&self) -> Vec<BodyId> {
crate::core::callback_state::assert_not_in_callback();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.body_ids()
}
#[cfg(feature = "serialize")]
pub fn body_ids_into(&self, out: &mut Vec<BodyId>) {
crate::core::callback_state::assert_not_in_callback();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.body_ids_into(out);
}
#[cfg(feature = "serialize")]
pub fn try_body_ids(&self) -> crate::error::ApiResult<Vec<BodyId>> {
crate::core::callback_state::check_not_in_callback()?;
let mut out = Vec::new();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.body_ids_into(&mut out);
Ok(out)
}
#[cfg(feature = "serialize")]
pub fn try_body_ids_into(&self, out: &mut Vec<BodyId>) -> crate::error::ApiResult<()> {
crate::core::callback_state::check_not_in_callback()?;
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.body_ids_into(out);
Ok(())
}
#[cfg(feature = "serialize")]
pub fn chain_records(&self) -> Vec<ChainCreateRecord> {
crate::core::callback_state::assert_not_in_callback();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.chain_records()
}
#[cfg(feature = "serialize")]
pub fn chain_records_into(&self, out: &mut Vec<ChainCreateRecord>) {
crate::core::callback_state::assert_not_in_callback();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.chain_records_into(out);
}
#[cfg(feature = "serialize")]
pub fn try_chain_records(&self) -> crate::error::ApiResult<Vec<ChainCreateRecord>> {
crate::core::callback_state::check_not_in_callback()?;
let mut out = Vec::new();
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.chain_records_into(&mut out);
Ok(out)
}
#[cfg(feature = "serialize")]
pub fn try_chain_records_into(
&self,
out: &mut Vec<ChainCreateRecord>,
) -> crate::error::ApiResult<()> {
crate::core::callback_state::check_not_in_callback()?;
self.core
.registries
.lock()
.expect("registries mutex poisoned")
.chain_records_into(out);
Ok(())
}
}
#[cfg(test)]
mod tests;