use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
use crate::body::Body;
use crate::error::ApiResult;
use crate::types::{BodyId, JointId, Vec2};
use crate::world::World;
use boxdd_sys::ffi;
use std::fmt;
use std::os::raw::c_void;
pub struct Joint<'w> {
pub(crate) id: ffi::b2JointId,
pub(crate) core: Arc<crate::core::world_core::WorldCore>,
pub(crate) _world: PhantomData<&'w World>,
}
impl fmt::Debug for Joint<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Joint").field("id", &self.id).finish()
}
}
pub struct OwnedJoint {
id: JointId,
core: Arc<crate::core::world_core::WorldCore>,
destroy_on_drop: bool,
wake_bodies_on_drop: bool,
_not_send: PhantomData<Rc<()>>,
}
impl OwnedJoint {
pub(crate) fn new(core: Arc<crate::core::world_core::WorldCore>, id: JointId) -> Self {
core.owned_joints
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Self {
id,
core,
destroy_on_drop: true,
wake_bodies_on_drop: true,
_not_send: PhantomData,
}
}
pub fn id(&self) -> JointId {
self.id
}
pub fn is_valid(&self) -> bool {
crate::core::callback_state::assert_not_in_callback();
unsafe { ffi::b2Joint_IsValid(self.id) }
}
pub fn try_is_valid(&self) -> ApiResult<bool> {
crate::core::callback_state::check_not_in_callback()?;
Ok(unsafe { ffi::b2Joint_IsValid(self.id) })
}
#[inline]
fn assert_valid(&self) {
crate::core::debug_checks::assert_joint_valid(self.id);
}
#[inline]
fn check_valid(&self) -> ApiResult<()> {
crate::core::debug_checks::check_joint_valid(self.id)
}
pub fn as_id(&self) -> JointId {
self.id
}
pub fn linear_separation(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetLinearSeparation(self.id) }
}
pub fn try_linear_separation(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetLinearSeparation(self.id) })
}
pub fn angular_separation(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetAngularSeparation(self.id) }
}
pub fn try_angular_separation(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetAngularSeparation(self.id) })
}
pub fn constraint_force(&self) -> Vec2 {
self.assert_valid();
Vec2::from(unsafe { ffi::b2Joint_GetConstraintForce(self.id) })
}
pub fn try_constraint_force(&self) -> ApiResult<Vec2> {
self.check_valid()?;
Ok(Vec2::from(unsafe {
ffi::b2Joint_GetConstraintForce(self.id)
}))
}
pub fn constraint_torque(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetConstraintTorque(self.id) }
}
pub fn try_constraint_torque(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetConstraintTorque(self.id) })
}
pub fn force_threshold(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetForceThreshold(self.id) }
}
pub fn try_force_threshold(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetForceThreshold(self.id) })
}
pub fn set_force_threshold(&mut self, threshold: f32) {
self.assert_valid();
unsafe { ffi::b2Joint_SetForceThreshold(self.id, threshold) }
}
pub fn try_set_force_threshold(&mut self, threshold: f32) -> ApiResult<()> {
self.check_valid()?;
unsafe { ffi::b2Joint_SetForceThreshold(self.id, threshold) }
Ok(())
}
pub fn torque_threshold(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetTorqueThreshold(self.id) }
}
pub fn try_torque_threshold(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetTorqueThreshold(self.id) })
}
pub fn set_torque_threshold(&mut self, threshold: f32) {
self.assert_valid();
unsafe { ffi::b2Joint_SetTorqueThreshold(self.id, threshold) }
}
pub fn try_set_torque_threshold(&mut self, threshold: f32) -> ApiResult<()> {
self.check_valid()?;
unsafe { ffi::b2Joint_SetTorqueThreshold(self.id, threshold) }
Ok(())
}
pub unsafe fn set_user_data_ptr(&mut self, p: *mut c_void) {
self.assert_valid();
let _ = self.core.clear_joint_user_data(self.id);
unsafe { ffi::b2Joint_SetUserData(self.id, p) }
}
pub unsafe fn try_set_user_data_ptr(&mut self, p: *mut c_void) -> ApiResult<()> {
self.check_valid()?;
let _ = self.core.clear_joint_user_data(self.id);
unsafe { ffi::b2Joint_SetUserData(self.id, p) }
Ok(())
}
pub fn user_data_ptr(&self) -> *mut c_void {
self.assert_valid();
unsafe { ffi::b2Joint_GetUserData(self.id) }
}
pub fn try_user_data_ptr(&self) -> ApiResult<*mut c_void> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetUserData(self.id) })
}
pub fn set_user_data<T: 'static>(&mut self, value: T) {
self.assert_valid();
let p = self.core.set_joint_user_data(self.id, value);
unsafe { ffi::b2Joint_SetUserData(self.id, p) };
}
pub fn try_set_user_data<T: 'static>(&mut self, value: T) -> ApiResult<()> {
self.check_valid()?;
let p = self.core.set_joint_user_data(self.id, value);
unsafe { ffi::b2Joint_SetUserData(self.id, p) };
Ok(())
}
pub fn clear_user_data(&mut self) -> bool {
self.assert_valid();
let had = self.core.clear_joint_user_data(self.id);
if had {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
had
}
pub fn try_clear_user_data(&mut self) -> ApiResult<bool> {
self.check_valid()?;
let had = self.core.clear_joint_user_data(self.id);
if had {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
Ok(had)
}
pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
self.assert_valid();
self.core
.try_with_joint_user_data(self.id, f)
.expect("user data type mismatch")
}
pub fn try_with_user_data<T: 'static, R>(
&self,
f: impl FnOnce(&T) -> R,
) -> ApiResult<Option<R>> {
self.check_valid()?;
self.core.try_with_joint_user_data(self.id, f)
}
pub fn with_user_data_mut<T: 'static, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
self.assert_valid();
self.core
.try_with_joint_user_data_mut(self.id, f)
.expect("user data type mismatch")
}
pub fn try_with_user_data_mut<T: 'static, R>(
&mut self,
f: impl FnOnce(&mut T) -> R,
) -> ApiResult<Option<R>> {
self.check_valid()?;
self.core.try_with_joint_user_data_mut(self.id, f)
}
pub fn take_user_data<T: 'static>(&mut self) -> Option<T> {
self.assert_valid();
let v = self
.core
.take_joint_user_data::<T>(self.id)
.expect("user data type mismatch");
if v.is_some() {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
v
}
pub fn try_take_user_data<T: 'static>(&mut self) -> ApiResult<Option<T>> {
self.check_valid()?;
let v = self.core.take_joint_user_data::<T>(self.id)?;
if v.is_some() {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
Ok(v)
}
pub fn wake_bodies_on_drop(mut self, flag: bool) -> Self {
self.wake_bodies_on_drop = flag;
self
}
pub fn into_id(mut self) -> JointId {
self.destroy_on_drop = false;
self.id
}
pub fn destroy(mut self, wake_bodies: bool) {
if self.destroy_on_drop && unsafe { ffi::b2Joint_IsValid(self.id) } {
if crate::core::callback_state::in_callback() || self.core.events_buffers_are_borrowed()
{
self.core
.defer_destroy(crate::core::world_core::DeferredDestroy::Joint {
id: self.id,
wake_bodies,
});
} else {
unsafe { ffi::b2DestroyJoint(self.id, wake_bodies) };
let _ = self.core.clear_joint_user_data(self.id);
}
}
self.destroy_on_drop = false;
}
}
impl Drop for OwnedJoint {
fn drop(&mut self) {
let _ = self.core.id;
let prev = self
.core
.owned_joints
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
debug_assert!(prev > 0, "owned joint counter underflow");
if self.destroy_on_drop && unsafe { ffi::b2Joint_IsValid(self.id) } {
if crate::core::callback_state::in_callback() || self.core.events_buffers_are_borrowed()
{
self.core
.defer_destroy(crate::core::world_core::DeferredDestroy::Joint {
id: self.id,
wake_bodies: self.wake_bodies_on_drop,
});
} else {
unsafe { ffi::b2DestroyJoint(self.id, self.wake_bodies_on_drop) };
let _ = self.core.clear_joint_user_data(self.id);
}
}
}
}
impl<'w> Joint<'w> {
pub(crate) fn new(core: Arc<crate::core::world_core::WorldCore>, id: JointId) -> Self {
Self {
id,
core,
_world: PhantomData,
}
}
#[inline]
fn assert_valid(&self) {
crate::core::debug_checks::assert_joint_valid(self.id);
}
#[inline]
fn check_valid(&self) -> ApiResult<()> {
crate::core::debug_checks::check_joint_valid(self.id)
}
pub fn id(&self) -> JointId {
self.id
}
pub fn is_valid(&self) -> bool {
crate::core::callback_state::assert_not_in_callback();
unsafe { ffi::b2Joint_IsValid(self.id) }
}
pub fn try_is_valid(&self) -> ApiResult<bool> {
crate::core::callback_state::check_not_in_callback()?;
Ok(unsafe { ffi::b2Joint_IsValid(self.id) })
}
pub fn linear_separation(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetLinearSeparation(self.id) }
}
pub fn try_linear_separation(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetLinearSeparation(self.id) })
}
pub fn angular_separation(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetAngularSeparation(self.id) }
}
pub fn try_angular_separation(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetAngularSeparation(self.id) })
}
pub fn constraint_force(&self) -> Vec2 {
self.assert_valid();
Vec2::from(unsafe { ffi::b2Joint_GetConstraintForce(self.id) })
}
pub fn try_constraint_force(&self) -> ApiResult<Vec2> {
self.check_valid()?;
Ok(Vec2::from(unsafe {
ffi::b2Joint_GetConstraintForce(self.id)
}))
}
pub fn constraint_torque(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetConstraintTorque(self.id) }
}
pub fn try_constraint_torque(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetConstraintTorque(self.id) })
}
pub fn force_threshold(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetForceThreshold(self.id) }
}
pub fn set_force_threshold(&mut self, threshold: f32) {
self.assert_valid();
unsafe { ffi::b2Joint_SetForceThreshold(self.id, threshold) }
}
pub fn try_force_threshold(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetForceThreshold(self.id) })
}
pub fn try_set_force_threshold(&mut self, threshold: f32) -> ApiResult<()> {
self.check_valid()?;
unsafe { ffi::b2Joint_SetForceThreshold(self.id, threshold) }
Ok(())
}
pub fn torque_threshold(&self) -> f32 {
self.assert_valid();
unsafe { ffi::b2Joint_GetTorqueThreshold(self.id) }
}
pub fn set_torque_threshold(&mut self, threshold: f32) {
self.assert_valid();
unsafe { ffi::b2Joint_SetTorqueThreshold(self.id, threshold) }
}
pub fn try_torque_threshold(&self) -> ApiResult<f32> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetTorqueThreshold(self.id) })
}
pub fn try_set_torque_threshold(&mut self, threshold: f32) -> ApiResult<()> {
self.check_valid()?;
unsafe { ffi::b2Joint_SetTorqueThreshold(self.id, threshold) }
Ok(())
}
pub unsafe fn set_user_data_ptr(&mut self, p: *mut c_void) {
self.assert_valid();
let _ = self.core.clear_joint_user_data(self.id);
unsafe { ffi::b2Joint_SetUserData(self.id, p) }
}
pub unsafe fn try_set_user_data_ptr(&mut self, p: *mut c_void) -> ApiResult<()> {
self.check_valid()?;
let _ = self.core.clear_joint_user_data(self.id);
unsafe { ffi::b2Joint_SetUserData(self.id, p) }
Ok(())
}
pub fn user_data_ptr(&self) -> *mut c_void {
self.assert_valid();
unsafe { ffi::b2Joint_GetUserData(self.id) }
}
pub fn try_user_data_ptr(&self) -> ApiResult<*mut c_void> {
self.check_valid()?;
Ok(unsafe { ffi::b2Joint_GetUserData(self.id) })
}
pub fn set_user_data<T: 'static>(&mut self, value: T) {
self.assert_valid();
let p = self.core.set_joint_user_data(self.id, value);
unsafe { ffi::b2Joint_SetUserData(self.id, p) };
}
pub fn try_set_user_data<T: 'static>(&mut self, value: T) -> ApiResult<()> {
self.check_valid()?;
let p = self.core.set_joint_user_data(self.id, value);
unsafe { ffi::b2Joint_SetUserData(self.id, p) };
Ok(())
}
pub fn clear_user_data(&mut self) -> bool {
self.assert_valid();
let had = self.core.clear_joint_user_data(self.id);
if had {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
had
}
pub fn try_clear_user_data(&mut self) -> ApiResult<bool> {
self.check_valid()?;
let had = self.core.clear_joint_user_data(self.id);
if had {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
Ok(had)
}
pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
self.assert_valid();
self.core
.try_with_joint_user_data(self.id, f)
.expect("user data type mismatch")
}
pub fn try_with_user_data<T: 'static, R>(
&self,
f: impl FnOnce(&T) -> R,
) -> ApiResult<Option<R>> {
self.check_valid()?;
self.core.try_with_joint_user_data(self.id, f)
}
pub fn with_user_data_mut<T: 'static, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
self.assert_valid();
self.core
.try_with_joint_user_data_mut(self.id, f)
.expect("user data type mismatch")
}
pub fn try_with_user_data_mut<T: 'static, R>(
&mut self,
f: impl FnOnce(&mut T) -> R,
) -> ApiResult<Option<R>> {
self.check_valid()?;
self.core.try_with_joint_user_data_mut(self.id, f)
}
pub fn take_user_data<T: 'static>(&mut self) -> Option<T> {
self.assert_valid();
let v = self
.core
.take_joint_user_data::<T>(self.id)
.expect("user data type mismatch");
if v.is_some() {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
v
}
pub fn try_take_user_data<T: 'static>(&mut self) -> ApiResult<Option<T>> {
self.check_valid()?;
let v = self.core.take_joint_user_data::<T>(self.id)?;
if v.is_some() {
unsafe { ffi::b2Joint_SetUserData(self.id, core::ptr::null_mut()) };
}
Ok(v)
}
pub fn destroy(self, wake_bodies: bool) {
crate::core::callback_state::assert_not_in_callback();
if unsafe { ffi::b2Joint_IsValid(self.id) } {
unsafe { ffi::b2DestroyJoint(self.id, wake_bodies) };
let _ = self.core.clear_joint_user_data(self.id);
}
}
pub fn try_destroy(self, wake_bodies: bool) -> ApiResult<()> {
self.check_valid()?;
if unsafe { ffi::b2Joint_IsValid(self.id) } {
unsafe { ffi::b2DestroyJoint(self.id, wake_bodies) };
let _ = self.core.clear_joint_user_data(self.id);
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct JointBase(pub(crate) ffi::b2JointDef);
impl Default for JointBase {
fn default() -> Self {
let mut base: ffi::b2JointDef = unsafe { core::mem::zeroed() };
base.drawScale = 1.0;
base.localFrameA = ffi::b2Transform {
p: ffi::b2Vec2 { x: 0.0, y: 0.0 },
q: ffi::b2Rot { c: 1.0, s: 0.0 },
};
base.localFrameB = ffi::b2Transform {
p: ffi::b2Vec2 { x: 0.0, y: 0.0 },
q: ffi::b2Rot { c: 1.0, s: 0.0 },
};
Self(base)
}
}
#[derive(Clone, Debug)]
pub struct JointBaseBuilder {
base: JointBase,
}
impl JointBaseBuilder {
pub fn new() -> Self {
Self {
base: JointBase::default(),
}
}
pub fn bodies<'w>(mut self, a: &Body<'w>, b: &Body<'w>) -> Self {
self.base.0.bodyIdA = a.id;
self.base.0.bodyIdB = b.id;
self
}
pub fn bodies_by_id(mut self, a: BodyId, b: BodyId) -> Self {
self.base.0.bodyIdA = a;
self.base.0.bodyIdB = b;
self
}
pub fn local_frames<VA: Into<crate::types::Vec2>, VB: Into<crate::types::Vec2>>(
mut self,
pos_a: VA,
angle_a: f32,
pos_b: VB,
angle_b: f32,
) -> Self {
let (sa, ca) = angle_a.sin_cos();
let (sb, cb) = angle_b.sin_cos();
self.base.0.localFrameA = ffi::b2Transform {
p: ffi::b2Vec2::from(pos_a.into()),
q: ffi::b2Rot { c: ca, s: sa },
};
self.base.0.localFrameB = ffi::b2Transform {
p: ffi::b2Vec2::from(pos_b.into()),
q: ffi::b2Rot { c: cb, s: sb },
};
self
}
pub fn collide_connected(mut self, flag: bool) -> Self {
self.base.0.collideConnected = flag;
self
}
pub fn force_threshold(mut self, v: f32) -> Self {
self.base.0.forceThreshold = v;
self
}
pub fn torque_threshold(mut self, v: f32) -> Self {
self.base.0.torqueThreshold = v;
self
}
pub fn constraint_hertz(mut self, v: f32) -> Self {
self.base.0.constraintHertz = v;
self
}
pub fn constraint_damping_ratio(mut self, v: f32) -> Self {
self.base.0.constraintDampingRatio = v;
self
}
pub fn draw_scale(mut self, v: f32) -> Self {
self.base.0.drawScale = v;
self
}
pub fn local_frames_raw(mut self, a: ffi::b2Transform, b: ffi::b2Transform) -> Self {
self.base.0.localFrameA = a;
self.base.0.localFrameB = b;
self
}
pub fn local_points_from_world<'w, V: Into<crate::types::Vec2>>(
mut self,
body_a: &Body<'w>,
world_a: V,
body_b: &Body<'w>,
world_b: V,
) -> Self {
let ta = body_a.transform();
let tb = body_b.transform();
let wa: ffi::b2Vec2 = world_a.into().into();
let wb: ffi::b2Vec2 = world_b.into().into();
let la = crate::core::math::world_to_local_point(ta, wa);
let lb = crate::core::math::world_to_local_point(tb, wb);
let ident = ffi::b2Transform {
p: ffi::b2Vec2 { x: 0.0, y: 0.0 },
q: ffi::b2Rot { c: 1.0, s: 0.0 },
};
let mut fa = ident;
let mut fb = ident;
fa.p = la;
fb.p = lb;
self.base.0.localFrameA = fa;
self.base.0.localFrameB = fb;
self
}
pub fn build(self) -> JointBase {
self.base
}
pub fn frames_from_world_with_axis<'w, VA, VB, AX>(
mut self,
body_a: &Body<'w>,
anchor_a_world: VA,
axis_world: AX,
body_b: &Body<'w>,
anchor_b_world: VB,
) -> Self
where
VA: Into<crate::types::Vec2>,
VB: Into<crate::types::Vec2>,
AX: Into<crate::types::Vec2>,
{
let ta = body_a.transform();
let tb = body_b.transform();
let wa: ffi::b2Vec2 = anchor_a_world.into().into();
let wb: ffi::b2Vec2 = anchor_b_world.into().into();
let axis_w: ffi::b2Vec2 = axis_world.into().into();
let la = crate::core::math::world_to_local_point(ta, wa);
let lb = crate::core::math::world_to_local_point(tb, wb);
let ra = crate::core::math::world_axis_to_local_rot(ta, axis_w);
let rb = crate::core::math::world_axis_to_local_rot(tb, axis_w);
self.base.0.localFrameA = ffi::b2Transform { p: la, q: ra };
self.base.0.localFrameB = ffi::b2Transform { p: lb, q: rb };
self
}
}
impl Default for JointBaseBuilder {
fn default() -> Self {
Self::new()
}
}