Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
405dae9
Use custom attributes for cable plasticity.
jumyungc Dec 20, 2025
5979e77
Added ball joints to VBD.
jumyungc Dec 31, 2025
14844bc
Merge branch 'refs/heads/main' into vbd-rigidbody-balljoint
jumyungc Dec 31, 2025
9a337e5
Added fixed joints to VBD.
jumyungc Jan 1, 2026
fedd35a
Added length normalization.
jumyungc Jan 2, 2026
7d549f2
Length normalization for stretch (Cosseart style)
jumyungc Jan 3, 2026
c3b7e8f
Minor quat sign update in the unit test.
jumyungc Jan 3, 2026
6ee90d2
Merge branch 'refs/heads/main' into vbd-rigidbody-balljoint
jumyungc Jan 12, 2026
4011218
Updated ReadMe. Minor cleanup.
jumyungc Jan 12, 2026
74ebd8a
Merge branch 'refs/heads/main' into vbd-rigidbody-balljoint
jumyungc Jan 12, 2026
a6015b4
Reverted unnecessary test changes.
jumyungc Jan 12, 2026
b020912
Added robustness: particle self contact working when contacts is none…
jumyungc Jan 12, 2026
6c75076
Updated minor comments.
jumyungc Jan 13, 2026
7d2be9d
Merge branch 'refs/heads/main' into vbd-rigidbody-balljoint
jumyungc Jan 13, 2026
46a2ad9
Updated minor comments.
jumyungc Jan 13, 2026
57063b8
Updated contact margin to align with recent margin computation change.
jumyungc Jan 13, 2026
f4ae028
Reverted empty contact object.
jumyungc Jan 13, 2026
9892101
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 13, 2026
29982f3
Removed dangling comments.
jumyungc Jan 13, 2026
e5d8cd6
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 13, 2026
d5fab33
Making coderabitai happy.
jumyungc Jan 14, 2026
b4a8fc7
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 14, 2026
7cb95d6
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 16, 2026
c015a39
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 18, 2026
2468ff6
Merge branch 'main' into vbd-rigidbody-balljoint
jumyungc Jan 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,39 @@ uv sync --extra examples
</tr>
</table>

### Cable Examples

<table>
<tr>
<td align="center" width="33%">
<a href="https://github.com/newton-physics/newton/blob/main/newton/examples/cable/example_cable_bend.py">
<img src="https://raw.githubusercontent.com/newton-physics/newton/main/docs/images/examples/example_cable_bend.jpg" alt="Cable Bend">
</a>
</td>
<td align="center" width="33%">
<a href="https://github.com/newton-physics/newton/blob/main/newton/examples/cable/example_cable_twist.py">
<img src="https://raw.githubusercontent.com/newton-physics/newton/main/docs/images/examples/example_cable_twist.jpg" alt="Cable Twist">
</a>
</td>
<td align="center" width="33%">
<a href="https://github.com/newton-physics/newton/blob/main/newton/examples/cable/example_cable_bundle_hysteresis.py">
<img src="https://raw.githubusercontent.com/newton-physics/newton/main/docs/images/examples/example_cable_bundle_hysteresis.jpg" alt="Cable Bundle Hysteresis">
</a>
</td>
</tr>
<tr>
<td align="center">
<code>uv run -m newton.examples cable_bend</code>
</td>
<td align="center">
<code>uv run -m newton.examples cable_twist</code>
</td>
<td align="center">
<code>uv run -m newton.examples cable_bundle_hysteresis</code>
</td>
</tr>
</table>

### Cloth Examples

<table>
Expand Down
Binary file added docs/images/examples/example_cable_bend.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/examples/example_cable_twist.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 45 additions & 13 deletions newton/_src/sim/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2951,10 +2951,15 @@ def add_joint_cable(
translation is the attachment point.
child_xform (Transform): The transform of the joint in the child body's local frame; its
translation is the attachment point.
stretch_stiffness: Linear stretch stiffness. If None, defaults to 1.0e9.
stretch_damping: Linear stretch damping. If None, defaults to 0.0.
bend_stiffness: Angular bend/twist stiffness. If None, defaults to 0.0.
bend_damping: Angular bend/twist damping. If None, defaults to 0.0.
stretch_stiffness: Cable stretch stiffness (stored as ``target_ke``) [N/m]. If None, defaults to 1.0e9.
stretch_damping: Cable stretch damping (stored as ``target_kd``). In :class:`newton.solvers.SolverVBD`
this is a dimensionless (Rayleigh-style) coefficient. If None,
defaults to 0.0.
bend_stiffness: Cable bend/twist stiffness (stored as ``target_ke``) [N*m] (torque per radian). If None,
defaults to 0.0.
bend_damping: Cable bend/twist damping (stored as ``target_kd``). In :class:`newton.solvers.SolverVBD`
this is a dimensionless (Rayleigh-style) coefficient. If None,
defaults to 0.0.
key: The key of the joint.
collision_filter_parent: Whether to filter collisions between shapes of the parent and child bodies.
enabled: Whether the joint is enabled.
Expand Down Expand Up @@ -4664,10 +4669,18 @@ def add_rod(
align the capsule's local +Z with the segment direction ``positions[i+1] - positions[i]``.
radius: Capsule radius.
cfg: Shape configuration for the capsules. If None, :attr:`default_shape_cfg` is used.
stretch_stiffness: Stretch stiffness for the cable joints. If None, defaults to 1.0e9.
stretch_damping: Stretch damping for the cable joints. If None, defaults to 0.0.
bend_stiffness: Bend/twist stiffness for the cable joints. If None, defaults to 0.0.
bend_damping: Bend/twist damping for the cable joints. If None, defaults to 0.0.
stretch_stiffness: Stretch stiffness for the cable joints. For rods, this is treated as a
material-like axial/shear stiffness (commonly interpreted as EA)
with units [N] and is internally converted to an effective point stiffness [N/m] by dividing by
segment length. If None, defaults to 1.0e9.
stretch_damping: Stretch damping for the cable joints (applied per-joint; not length-normalized). If None,
defaults to 0.0.
bend_stiffness: Bend/twist stiffness for the cable joints. For rods, this is treated as a
material-like bending/twist stiffness (e.g., EI) with units [N*m^2] and is internally converted to
an effective per-joint stiffness [N*m] (torque per radian) by dividing by segment length. If None,
defaults to 0.0.
bend_damping: Bend/twist damping for the cable joints (applied per-joint; not length-normalized). If None,
defaults to 0.0.
closed: If True, connects the last segment back to the first to form a closed loop. If False,
creates an open chain. Note: rods require at least 2 segments.
key: Optional key prefix for bodies, shapes, and joints.
Expand All @@ -4692,6 +4705,9 @@ def add_rod(
Note:
- Bend defaults are 0.0 (no bending resistance unless specified). Stretch defaults to a high
stiffness (1.0e9), which keeps neighboring capsules closely coupled (approximately inextensible).
- Internally, stretch and bend stiffnesses are pre-scaled by dividing by segment length so solver kernels
do not need per-segment length normalization.
- Damping values are passed through as provided (per joint) and are not length-normalized.
- Each segment is implemented as a capsule primitive. The segment's body transform is
placed at the start point ``positions[i]`` with a local center-of-mass offset of
``(0, 0, half_height)`` so that the COM lies at the segment midpoint. The capsule shape
Expand All @@ -4709,6 +4725,11 @@ def add_rod(
bend_damping = 0.0 if bend_damping is None else bend_damping

# Input validation
if stretch_stiffness < 0.0 or bend_stiffness < 0.0:
raise ValueError("add_rod: stretch_stiffness and bend_stiffness must be >= 0")

# Guard against near-zero lengths: segment length is used to normalize stiffness later (EA/L, EI/L).
min_segment_length = 1.0e-9
num_segments = len(quaternions)
if len(positions) != num_segments + 1:
raise ValueError(
Expand All @@ -4735,10 +4756,10 @@ def add_rod(

# Calculate segment properties
segment_length = wp.length(p1 - p0)
if segment_length <= 0.0:
if segment_length <= min_segment_length:
raise ValueError(
f"add_rod: segment {i} has zero or negative length; "
"positions must form strictly positive-length segments"
f"add_rod: segment {i} has a too-small length (length={float(segment_length):.3e}); "
f"segment length must be > {min_segment_length:.1e}"
)
segment_lengths.append(float(segment_length))
half_height = 0.5 * segment_length
Expand Down Expand Up @@ -4808,14 +4829,25 @@ def add_rod(
# Joint key: numbered 1 through num_joints
joint_key = f"{key}_cable_{i + 1}" if key else None

# Pre-scale rod stiffnesses here so solver kernels do not need per-segment length normalization.
# Use the parent segment length L.
#
# - Stretch: treat the user input as a material-like axial/shear stiffness (commonly EA) [N]
# and store an effective per-joint (point-to-point) spring stiffness k_eff = EA / L [N/m].
# - Bend/twist: treat the user input as a material-like bending/twist stiffness (commonly EI) [N*m^2]
# and store an effective per-joint angular stiffness k_eff = EI / L [N*m].
seg_len = segment_lengths[parent_idx]
stretch_ke_eff = stretch_stiffness / seg_len
bend_ke_eff = bend_stiffness / seg_len

joint = self.add_joint_cable(
parent=parent_body,
child=child_body,
parent_xform=parent_xform,
child_xform=child_xform,
bend_stiffness=bend_stiffness,
bend_stiffness=bend_ke_eff,
bend_damping=bend_damping,
stretch_stiffness=stretch_stiffness,
stretch_stiffness=stretch_ke_eff,
stretch_damping=stretch_damping,
key=joint_key,
collision_filter_parent=True,
Expand Down
Loading
Loading