Affine Body Revolute Joint External Force
#668 AffineBodyRevoluteJointExternalForce
The Affine Body Revolute Joint External Force applies a scalar external torque around the axis of a Revolute Joint. The torque drives rotational motion between the two connected affine bodies about the shared joint axis.
Torque Application
Given a scalar torque \(\tau\) and the per-body joint axis \(\hat{\mathbf{e}}_k\), the external torque is mapped via the principle of virtual work directly to an equivalent generalized force on the affine rotational DOF of each body — without forming a translational force or evaluating a center-of-mass lever arm. For each body \(k \in \{i, j\}\) with affine matrix \(\mathbf{A}_k \in \mathbb{R}^{3 \times 3}\) extracted from \(\mathbf{q}_k\):
where:
- \(\tau\) is the scalar torque magnitude (per edge)
- \(\hat{\mathbf{e}}_k\) is the normalized joint axis on body \(k\) in world space (see Axis Direction)
- \([\hat{\mathbf{e}}_k]_\times \in \mathbb{R}^{3 \times 3}\) is the skew-symmetric matrix satisfying \([\hat{\mathbf{e}}_k]_\times \mathbf{w} = \hat{\mathbf{e}}_k \times \mathbf{w}\)
- \(\mathbf{A}_k^{-T}\) is the inverse-transpose of body \(k\)'s current affine matrix
- \(\operatorname{vec}(\cdot)\) packs a \(3 \times 3\) matrix into the \(9\) components of \(\mathbf{q}_k[3{:}12]\) in row-major order, matching the row-major layout used for the rows of \(\mathbf{A}_k\) in \(\mathbf{q}_k\)
- \(\mathbf{0}_3\) denotes the three-dimensional zero vector — a pure torque produces no translational generalized force, regardless of whether \(\mathbf{x}_k\) is the COM or a mesh anchor
The same factor \(\tau\) is applied to both bodies; Newton's third law is encoded by the strict antisymmetry \(\hat{\mathbf{e}}_i = -\hat{\mathbf{e}}_j\) that is enforced numerically in § Axis Direction, so the opposite axis direction supplies the opposite-sign reaction.
Reduction to rigid body
When \(\mathbf{A}_k \in SO(3)\) (no shear or scale, i.e. an undeformed rigid body), \(\mathbf{A}_k^{-T} = \mathbf{A}_k\) and the formula reduces to the familiar rigid-body torque-on-rotation generalized force \(\mathbf{F}^A_k = \tfrac{1}{2}[\hat{\mathbf{e}}_k]_\times \mathbf{A}_k\). The factor \(\mathbf{A}_k^{-T}\) is the affine-body correction for shear/scale; it vanishes in the rigid limit.
Axis Direction
The joint axis on body \(k\) is defined by two anchor points \(\mathbf{x}^0_k\) and \(\mathbf{x}^1_k\) in world space, following the base Revolute Joint convention. Each body first computes its own raw world-space axis from its own anchors and current pose:
By construction the two anchor pairs are laid out in opposite order on the two bodies, so \(\tilde{\mathbf{e}}_i \approx -\tilde{\mathbf{e}}_j\) whenever the joint constraint is satisfied. To absorb residual numerical drift near the singularity (and to guarantee Newton's third law bit-exactly regardless of the per-body roundoff), the two raw axes are symmetrized into one strictly antisymmetric pair:
The averaging step is a first-order denoiser: as long as the joint is well-posed, \(\tilde{\mathbf{e}}_i + \tilde{\mathbf{e}}_j\) is roundoff-level and the corrected axes lie within the same \(\mathcal{O}(\varepsilon)\) neighborhood as the raw ones, while strict antisymmetry \(\hat{\mathbf{e}}_i = -\hat{\mathbf{e}}_j\) now holds exactly. If the magnitude of \(\tilde{\mathbf{e}}_i + \tilde{\mathbf{e}}_j\) is non-trivial, the joint constraint itself has drifted and the upstream constraint solver — not this symmetrization — is responsible for the fix.
Parent vs. child axis and positive torque
Parent \(=\) left \((i)\), child \(=\) right \((j)\), matching the base joint. The two anchor pairs are laid out in opposite order on the two bodies, so the raw per-body axes point opposite ways: \(\tilde{\mathbf{e}}_i \approx -\tilde{\mathbf{e}}_j\). After the symmetrization in § Axis Direction this becomes the exact identity \(\hat{\mathbf{e}}_i = -\hat{\mathbf{e}}_j\).
external_torque is child-positive: a positive \(\tau\) applies a right-hand torque about \(\hat{\mathbf{e}}_j\) (the child axis); the parent receives the equal-and-opposite reaction automatically because \(\hat{\mathbf{e}}_i = -\hat{\mathbf{e}}_j\). This polarity applies only to this constitution (UID 668); the shared angle frame for limits and driving targets follows the base Revolute Joint.
Time Integration
external_torque contributes no potential energy of its own. The generalized force \(\mathbf{F}_k\) from § Torque Application enters the time step only through the predicted position \(\tilde{\mathbf{q}}_k\) (i.e. as an inertial shift), in the same way as AffineBodyExternalForce:
Runtime Control
The torque can be updated at each frame through the Animator system:
- Set
external_torque/is_constrainedto1to enable the torque, or0to disable it. - Modify the
external_torqueattribute to change the torque magnitude.
Requirement
This constitution must be applied to a geometry that already has an AffineBodyRevoluteJoint (UID=18) constitution.
Attributes
On the joint geometry (1D simplicial complex), on edges (one edge per joint). Linking and state fields are inherited from the base Affine Body Revolute Joint. The fields owned by this constitution are:
external_torque: \(\tau\) in the formulae above, scalar torque around the joint axis (one per edge)external_torque/is_constrained: enables (1) or disables (0) the external torque