From 932fe6c976e8aa34b6830513c461f3d029c3816a Mon Sep 17 00:00:00 2001 From: Ki Date: Mon, 11 May 2026 02:19:09 -0400 Subject: [PATCH] feat: Add frame setter methods for revolute/prismatic joints This implements the frame transformation capabilities requested in issue #271, allowing users to set joint frames after joint creation via: - setFrameX1() / setFrameX2() for rotation frames - setLocalFrame1() / setLocalFrame2() for full frame (anchor + rotation) These methods enable more flexible joint configuration in physics simulations, particularly for use cases like VistaKine where joint frames need adjustment after initial setup. Fixes #271 --- src.ts/dynamics/impulse_joint.ts | 42 ++++++++++++++++++++++++++++++++ src/dynamics/impulse_joint.rs | 29 ++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src.ts/dynamics/impulse_joint.ts b/src.ts/dynamics/impulse_joint.ts index 0c637ddf..1ea0177f 100644 --- a/src.ts/dynamics/impulse_joint.ts +++ b/src.ts/dynamics/impulse_joint.ts @@ -210,6 +210,48 @@ export class ImpulseJoint { rawPoint.free(); } + // #if DIM3 + /** + * Sets the rotation quaternion that aligns this joint's first local axis to the `x` axis. + */ + public setFrameX1(rot: Rotation) { + const rawRot = RotationOps.intoRaw(rot); + this.rawSet.jointSetFrameX1(this.handle, rawRot); + rawRot.free(); + } + + /** + * Sets the rotation quaternion that aligns this joint's second local axis to the `x` axis. + */ + public setFrameX2(rot: Rotation) { + const rawRot = RotationOps.intoRaw(rot); + this.rawSet.jointSetFrameX2(this.handle, rawRot); + rawRot.free(); + } + + /** + * Sets the full local frame (anchor position and rotation) for the first rigid-body attachment. + */ + public setLocalFrame1(anchor: Vector, rot: Rotation) { + const rawAnchor = VectorOps.intoRaw(anchor); + const rawRot = RotationOps.intoRaw(rot); + this.rawSet.jointSetLocalFrame1(this.handle, rawAnchor, rawRot); + rawAnchor.free(); + rawRot.free(); + } + + /** + * Sets the full local frame (anchor position and rotation) for the second rigid-body attachment. + */ + public setLocalFrame2(anchor: Vector, rot: Rotation) { + const rawAnchor = VectorOps.intoRaw(anchor); + const rawRot = RotationOps.intoRaw(rot); + this.rawSet.jointSetLocalFrame2(this.handle, rawAnchor, rawRot); + rawAnchor.free(); + rawRot.free(); + } + // #endif + /** * Controls whether contacts are computed between colliders attached * to the rigid-bodies linked by this joint. diff --git a/src/dynamics/impulse_joint.rs b/src/dynamics/impulse_joint.rs index bfbdf128..a9519a0f 100644 --- a/src/dynamics/impulse_joint.rs +++ b/src/dynamics/impulse_joint.rs @@ -2,6 +2,7 @@ use crate::dynamics::{RawImpulseJointSet, RawJointAxis, RawJointType, RawMotorMo use crate::math::{RawRotation, RawVector}; use crate::utils::{self, FlatHandle}; use rapier::dynamics::JointAxis; +use rapier::math::Isometry; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -61,6 +62,34 @@ impl RawImpulseJointSet { }) } + /// Sets the angular part of the joint's local frame relative to the first rigid-body. + pub fn jointSetFrameX1(&mut self, handle: FlatHandle, newRot: &RawRotation) { + self.map_mut(handle, |j| { + j.data.local_frame1.rotation = newRot.0; + }); + } + + /// Sets the angular part of the joint's local frame relative to the second rigid-body. + pub fn jointSetFrameX2(&mut self, handle: FlatHandle, newRot: &RawRotation) { + self.map_mut(handle, |j| { + j.data.local_frame2.rotation = newRot.0; + }); + } + + /// Sets the full local frame (anchor + rotation) for the first rigid-body attachment. + pub fn jointSetLocalFrame1(&mut self, handle: FlatHandle, anchor: &RawVector, rot: &RawRotation) { + self.map_mut(handle, |j| { + j.data.set_local_frame1(Isometry::from_parts(anchor.0.into(), rot.0)); + }); + } + + /// Sets the full local frame (anchor + rotation) for the second rigid-body attachment. + pub fn jointSetLocalFrame2(&mut self, handle: FlatHandle, anchor: &RawVector, rot: &RawRotation) { + self.map_mut(handle, |j| { + j.data.set_local_frame2(Isometry::from_parts(anchor.0.into(), rot.0)); + }); + } + /// Are contacts between the rigid-bodies attached by this joint enabled? pub fn jointContactsEnabled(&self, handle: FlatHandle) -> bool { self.map(handle, |j| j.data.contacts_enabled)