use crate::prelude::*;
use bevy::prelude::*;
#[derive(Component, Clone, Copy, Debug, PartialEq)]
pub struct RevoluteJoint {
pub entity1: Entity,
pub entity2: Entity,
pub local_anchor1: Vector,
pub local_anchor2: Vector,
#[cfg(feature = "2d")]
pub(crate) aligned_axis: Vector3,
#[cfg(feature = "3d")]
pub aligned_axis: Vector,
pub angle_limit: Option<AngleLimit>,
pub damping_linear: Scalar,
pub damping_angular: Scalar,
pub position_lagrange: Scalar,
pub align_lagrange: Scalar,
pub angle_limit_lagrange: Scalar,
pub compliance: Scalar,
pub force: Vector,
pub align_torque: Torque,
pub angle_limit_torque: Torque,
}
impl XpbdConstraint<2> for RevoluteJoint {
fn entities(&self) -> [Entity; 2] {
[self.entity1, self.entity2]
}
fn clear_lagrange_multipliers(&mut self) {
self.position_lagrange = 0.0;
self.align_lagrange = 0.0;
self.angle_limit_lagrange = 0.0;
}
fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
let [body1, body2] = bodies;
let compliance = self.compliance;
let dq = self.get_delta_q(&body1.rotation, &body2.rotation);
let mut lagrange = self.align_lagrange;
self.align_torque = self.align_orientation(body1, body2, dq, &mut lagrange, compliance, dt);
self.align_lagrange = lagrange;
let mut lagrange = self.position_lagrange;
self.force = self.align_position(
body1,
body2,
self.local_anchor1,
self.local_anchor2,
&mut lagrange,
compliance,
dt,
);
self.position_lagrange = lagrange;
self.angle_limit_torque = self.apply_angle_limits(body1, body2, dt);
}
}
impl Joint for RevoluteJoint {
fn new(entity1: Entity, entity2: Entity) -> Self {
Self {
entity1,
entity2,
local_anchor1: Vector::ZERO,
local_anchor2: Vector::ZERO,
aligned_axis: Vector3::Z,
angle_limit: None,
damping_linear: 1.0,
damping_angular: 1.0,
position_lagrange: 0.0,
align_lagrange: 0.0,
angle_limit_lagrange: 0.0,
compliance: 0.0,
force: Vector::ZERO,
#[cfg(feature = "2d")]
align_torque: 0.0,
#[cfg(feature = "3d")]
align_torque: Vector::ZERO,
#[cfg(feature = "2d")]
angle_limit_torque: 0.0,
#[cfg(feature = "3d")]
angle_limit_torque: Vector::ZERO,
}
}
fn with_compliance(self, compliance: Scalar) -> Self {
Self { compliance, ..self }
}
fn with_local_anchor_1(self, anchor: Vector) -> Self {
Self {
local_anchor1: anchor,
..self
}
}
fn with_local_anchor_2(self, anchor: Vector) -> Self {
Self {
local_anchor2: anchor,
..self
}
}
fn with_linear_velocity_damping(self, damping: Scalar) -> Self {
Self {
damping_linear: damping,
..self
}
}
fn with_angular_velocity_damping(self, damping: Scalar) -> Self {
Self {
damping_angular: damping,
..self
}
}
fn damping_linear(&self) -> Scalar {
self.damping_linear
}
fn damping_angular(&self) -> Scalar {
self.damping_angular
}
}
impl RevoluteJoint {
#[cfg(feature = "3d")]
pub fn with_aligned_axis(self, axis: Vector) -> Self {
Self {
aligned_axis: axis,
..self
}
}
pub fn with_angle_limits(self, min: Scalar, max: Scalar) -> Self {
Self {
angle_limit: Some(AngleLimit::new(min, max)),
..self
}
}
fn get_delta_q(&self, rot1: &Rotation, rot2: &Rotation) -> Vector3 {
let a1 = rot1.rotate_vec3(self.aligned_axis);
let a2 = rot2.rotate_vec3(self.aligned_axis);
a1.cross(a2)
}
#[allow(clippy::too_many_arguments)]
fn apply_angle_limits(
&mut self,
body1: &mut RigidBodyQueryItem,
body2: &mut RigidBodyQueryItem,
dt: Scalar,
) -> Torque {
if let Some(angle_limit) = self.angle_limit {
let limit_axis = Vector3::new(
self.aligned_axis.z,
self.aligned_axis.x,
self.aligned_axis.y,
);
let a1 = body1.rotation.rotate_vec3(limit_axis);
let a2 = body2.rotation.rotate_vec3(limit_axis);
let n = a1.cross(a2).normalize();
if let Some(dq) = angle_limit.compute_correction(n, a1, a2, PI) {
let mut lagrange = self.angle_limit_lagrange;
let torque =
self.align_orientation(body1, body2, dq, &mut lagrange, self.compliance, dt);
self.angle_limit_lagrange = lagrange;
return torque;
}
}
Torque::ZERO
}
}
impl PositionConstraint for RevoluteJoint {}
impl AngularConstraint for RevoluteJoint {}