use crate::error::{Error, Result};
use crate::joints::runtime::JointWrite;
use crate::types::{BodyId, JointId, Vec2};
use boxdd_sys::ffi;
use std::os::raw::c_void;
mod scoped;
mod user_data;
use self::user_data::*;
pub struct Joint<'w> {
pub(crate) proof: crate::world::JointProof<'w>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum JointType {
Distance,
Filter,
Motor,
Prismatic,
Revolute,
Weld,
Wheel,
}
impl JointType {
#[inline]
pub const fn from_raw(raw: ffi::b2JointType) -> Option<Self> {
match raw {
ffi::b2JointType_b2_distanceJoint => Some(Self::Distance),
ffi::b2JointType_b2_filterJoint => Some(Self::Filter),
ffi::b2JointType_b2_motorJoint => Some(Self::Motor),
ffi::b2JointType_b2_prismaticJoint => Some(Self::Prismatic),
ffi::b2JointType_b2_revoluteJoint => Some(Self::Revolute),
ffi::b2JointType_b2_weldJoint => Some(Self::Weld),
ffi::b2JointType_b2_wheelJoint => Some(Self::Wheel),
_ => None,
}
}
#[inline]
pub const fn into_raw(self) -> ffi::b2JointType {
match self {
Self::Distance => ffi::b2JointType_b2_distanceJoint,
Self::Filter => ffi::b2JointType_b2_filterJoint,
Self::Motor => ffi::b2JointType_b2_motorJoint,
Self::Prismatic => ffi::b2JointType_b2_prismaticJoint,
Self::Revolute => ffi::b2JointType_b2_revoluteJoint,
Self::Weld => ffi::b2JointType_b2_weldJoint,
Self::Wheel => ffi::b2JointType_b2_wheelJoint,
}
}
}
impl TryFrom<ffi::b2JointType> for JointType {
type Error = ffi::b2JointType;
#[inline]
fn try_from(value: ffi::b2JointType) -> std::result::Result<Self, Self::Error> {
Self::from_raw(value).ok_or(value)
}
}
#[inline]
fn raw_joint_id(id: JointId) -> ffi::b2JointId {
id.into_raw()
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct ConstraintTuning {
hertz: f32,
damping_ratio: f32,
}
impl ConstraintTuning {
#[inline]
pub fn new(hertz: f32, damping_ratio: f32) -> Result<Self> {
let tuning = Self {
hertz,
damping_ratio,
};
super::check_joint_tuning(tuning, "ConstraintTuning::new", "hertz/damping_ratio")?;
Ok(tuning)
}
#[inline]
pub const fn hertz(self) -> f32 {
self.hertz
}
#[inline]
pub const fn damping_ratio(self) -> f32 {
self.damping_ratio
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for ConstraintTuning {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct Repr {
hertz: f32,
damping_ratio: f32,
}
let repr = <Repr as serde::Deserialize>::deserialize(deserializer)?;
Self::new(repr.hertz, repr.damping_ratio).map_err(serde::de::Error::custom)
}
}
#[inline]
pub(crate) fn joint_body_a_id_in_impl(joint: crate::world::JointCall<'_>) -> Result<BodyId> {
let raw = unsafe { ffi::b2Joint_GetBodyA(raw_joint_id(joint.id())) };
joint.with_output_identity_resolver(|resolver| resolver.active_body(raw))
}
#[inline]
pub(crate) fn joint_body_b_id_in_impl(joint: crate::world::JointCall<'_>) -> Result<BodyId> {
let raw = unsafe { ffi::b2Joint_GetBodyB(raw_joint_id(joint.id())) };
joint.with_output_identity_resolver(|resolver| resolver.active_body(raw))
}
#[inline]
pub(crate) fn joint_linear_separation_impl(id: JointId) -> Result<f32> {
super::check_native_joint_finite(
unsafe { ffi::b2Joint_GetLinearSeparation(raw_joint_id(id)) },
"Joint::linear_separation",
"linear_separation",
)
}
#[inline]
pub(crate) fn joint_angular_separation_impl(id: JointId) -> Result<f32> {
super::check_native_joint_finite(
unsafe { ffi::b2Joint_GetAngularSeparation(raw_joint_id(id)) },
"Joint::angular_separation",
"angular_separation",
)
}
#[inline]
pub(crate) fn joint_constraint_force_impl(id: JointId) -> Result<Vec2> {
super::check_native_joint_vec2(
Vec2::from_raw(unsafe { ffi::b2Joint_GetConstraintForce(raw_joint_id(id)) }),
"Joint::constraint_force",
"constraint_force",
)
}
#[inline]
pub(crate) fn joint_constraint_torque_impl(id: JointId) -> Result<f32> {
super::check_native_joint_finite(
unsafe { ffi::b2Joint_GetConstraintTorque(raw_joint_id(id)) },
"Joint::constraint_torque",
"constraint_torque",
)
}
#[inline]
pub(crate) fn joint_collide_connected_impl(id: JointId) -> bool {
unsafe { ffi::b2Joint_GetCollideConnected(raw_joint_id(id)) }
}
#[inline]
pub(crate) fn joint_constraint_tuning_impl(id: JointId) -> Result<ConstraintTuning> {
let mut hertz = 0.0f32;
let mut damping_ratio = 0.0f32;
unsafe { ffi::b2Joint_GetConstraintTuning(raw_joint_id(id), &mut hertz, &mut damping_ratio) };
ConstraintTuning::new(hertz, damping_ratio).map_err(|_| Error::InvalidNativeOutput {
operation: "Joint::constraint_tuning",
output: "constraint_tuning",
constraint: "finite non-negative hertz and damping ratio values",
})
}
#[inline]
pub(crate) fn joint_local_frame_a_impl(id: JointId) -> Result<crate::Transform> {
crate::Transform::from_raw(unsafe { ffi::b2Joint_GetLocalFrameA(raw_joint_id(id)) }).map_err(
|_| Error::InvalidNativeOutput {
operation: "Joint::local_frame_a",
output: "local_frame_a",
constraint: "a finite rigid transform",
},
)
}
#[inline]
pub(crate) fn joint_local_frame_b_impl(id: JointId) -> Result<crate::Transform> {
crate::Transform::from_raw(unsafe { ffi::b2Joint_GetLocalFrameB(raw_joint_id(id)) }).map_err(
|_| Error::InvalidNativeOutput {
operation: "Joint::local_frame_b",
output: "local_frame_b",
constraint: "a finite rigid transform",
},
)
}
#[inline]
pub(crate) fn joint_force_threshold_impl(id: JointId) -> Result<f32> {
super::check_native_joint_non_negative(
unsafe { ffi::b2Joint_GetForceThreshold(raw_joint_id(id)) },
"Joint::force_threshold",
"force_threshold",
)
}
#[inline]
pub(crate) fn joint_torque_threshold_impl(id: JointId) -> Result<f32> {
super::check_native_joint_non_negative(
unsafe { ffi::b2Joint_GetTorqueThreshold(raw_joint_id(id)) },
"Joint::torque_threshold",
"torque_threshold",
)
}
impl Joint<'_> {
#[inline]
pub(crate) const fn cached_kind(&self) -> JointType {
self.proof.kind()
}
#[inline]
fn joint_id(&self) -> JointId {
self.proof.id()
}
#[inline]
fn joint_access(&self) -> &crate::world::JointProof<'_> {
&self.proof
}
pub fn joint_type(&self) -> Result<JointType> {
self.joint_access().call(|joint| Ok(joint.kind()))
}
pub fn body_a_id(&self) -> Result<BodyId> {
self.joint_access().call(joint_body_a_id_in_impl)
}
pub fn body_b_id(&self) -> Result<BodyId> {
self.joint_access().call(joint_body_b_id_in_impl)
}
pub fn collide_connected(&self) -> Result<bool> {
self.joint_access()
.call(|_| Ok(joint_collide_connected_impl(self.joint_id())))
}
pub fn set_collide_connected(&mut self, flag: bool) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetCollideConnected(flag).apply(self.joint_id()))
}
pub fn constraint_tuning(&self) -> Result<ConstraintTuning> {
self.joint_access()
.call(|_| joint_constraint_tuning_impl(self.joint_id()))
}
pub fn set_constraint_tuning(&mut self, tuning: ConstraintTuning) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetConstraintTuning(tuning).apply(self.joint_id()))
}
pub fn local_frame_a(&self) -> Result<crate::Transform> {
self.joint_access()
.call(|_| joint_local_frame_a_impl(self.joint_id()))
}
pub fn local_frame_b(&self) -> Result<crate::Transform> {
self.joint_access()
.call(|_| joint_local_frame_b_impl(self.joint_id()))
}
pub fn set_local_frame_a(&mut self, frame: crate::Transform) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetLocalFrameA(frame).apply(self.joint_id()))
}
pub fn set_local_frame_b(&mut self, frame: crate::Transform) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetLocalFrameB(frame).apply(self.joint_id()))
}
pub fn wake_bodies(&mut self) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::WakeBodies.apply(self.joint_id()))
}
pub fn linear_separation(&self) -> Result<f32> {
self.joint_access()
.call(|_| joint_linear_separation_impl(self.joint_id()))
}
pub fn angular_separation(&self) -> Result<f32> {
self.joint_access()
.call(|_| joint_angular_separation_impl(self.joint_id()))
}
pub fn constraint_force(&self) -> Result<Vec2> {
self.joint_access()
.call(|_| joint_constraint_force_impl(self.joint_id()))
}
pub fn constraint_torque(&self) -> Result<f32> {
self.joint_access()
.call(|_| joint_constraint_torque_impl(self.joint_id()))
}
pub fn force_threshold(&self) -> Result<f32> {
self.joint_access()
.call(|_| joint_force_threshold_impl(self.joint_id()))
}
pub fn set_force_threshold(&mut self, threshold: f32) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetForceThreshold(threshold).apply(self.joint_id()))
}
pub fn torque_threshold(&self) -> Result<f32> {
self.joint_access()
.call(|_| joint_torque_threshold_impl(self.joint_id()))
}
pub fn set_torque_threshold(&mut self, threshold: f32) -> Result<()> {
self.joint_access()
.call(|_| JointWrite::SetTorqueThreshold(threshold).apply(self.joint_id()))
}
pub fn set_user_data_ptr_raw(&mut self, p: *mut c_void) -> Result<()> {
self.joint_access()
.call(|joint| joint_set_user_data_ptr_impl(joint, p))
}
pub fn user_data_ptr_raw(&self) -> Result<*mut c_void> {
let id = self.joint_id();
self.joint_access()
.call(|_| Ok(joint_user_data_ptr_impl(id)))
}
pub fn set_user_data<T: 'static>(&mut self, value: T) -> Result<()> {
let value = crate::core::callback_state::PendingUserValue::new(value);
self.joint_access()
.call(move |joint| joint_set_user_data_impl(joint, value))
}
pub fn clear_user_data(&mut self) -> Result<bool> {
self.joint_access().call(joint_clear_user_data_impl)
}
pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<Option<R>> {
let f = crate::core::callback_state::PendingUserValue::new(f);
self.joint_access()
.call(move |joint| joint_with_user_data_impl(joint, f))
}
pub fn with_user_data_mut<T: 'static, R>(
&mut self,
f: impl FnOnce(&mut T) -> R,
) -> Result<Option<R>> {
let f = crate::core::callback_state::PendingUserValue::new(f);
self.joint_access()
.call(move |joint| joint_with_user_data_mut_impl(joint, f))
}
pub fn take_user_data<T: 'static>(&mut self) -> Result<Option<T>> {
self.joint_access().call(joint_take_user_data_impl::<T>)
}
}