How to Configure Strain Limiting Parameters to Prevent Mesh Deformation in PPF Contact Solver

To configure strain limiting in the PPF Contact Solver, set enable_strain_limit to True and specify a strain_limit value (e.g., 0.05 for 5% stretch), but you must keep shrink_x and shrink_y at 1.0 to avoid validation conflicts that raise a ValueError.

The st-tech/ppf-contact-solver repository implements strain limiting as a per-material constraint that prevents shell and rod meshes from stretching beyond defined thresholds during physics simulation. Configuring these parameters correctly ensures your simulations maintain mesh integrity without inverted faces or excessive distortion.

Understanding Strain Limiting Parameters

Strain limiting in PPF Contact Solver acts as a hard constraint on edge-length changes during simulation. The system stores these parameters per object group (shell or rod) and validates configurations at scene-build time to prevent incompatible settings.

According to blender_addon/models/groups.py, every object group initializes with conservative defaults:

  • enable_strain_limit = False (feature disabled by default)
  • strain_limit = 0.05 (5% maximum stretch when enabled)

The Python ParamHolder class defined in frontend/_param_.py wraps the Rust backing store, exposing typed set() and get() methods for these parameters. When you configure strain limiting, you are setting a multiplicative factor—for example, a strain_limit of 0.08 permits up to 8% elongation from the rest shape before the solver intervenes.

Configuring Strain Limiting via Python API

When working headless or scripting simulation setups, use the SceneBuilder API to configure strain constraints programmatically.

First, enable strain limiting and set the threshold:

from frontend import SceneBuilder

builder = SceneBuilder()
scene = builder.load("simulation_scene.json")

# Access your shell object

cloth = scene.object["cloth_shell"]

# Enable strain limiting with an 8% stretch tolerance

cloth.param.set("enable_strain_limit", True)
cloth.param.set("strain_limit", 0.08)

Then, ensure shrink parameters remain neutral to pass validation:


# Required: keep shrink at unity when strain limiting is active

cloth.param.set("shrink_x", 1.0)
cloth.param.set("shrink_y", 1.0)

# Build and solve

solution = scene.solve()

For quick testing, you can also use the hyphenated key variant that the Rust side normalizes:


# Alternative key syntax (also valid)

cloth.param.set("strain-limit", 0.04)

Configuring Strain Limiting via Blender UI

The Blender addon exposes these parameters in the Object Group panel without requiring code. According to blender_addon/ui/object_group.py and blender_addon/ui/dynamics/panels.py, you can configure settings through the interface:

  1. Select your cloth object group in the Outliner
  2. In the Object Group panel, tick "Enable Strain Limit"
  3. Adjust the "Strain Limit" slider (e.g., 0.05 for 5% stretch)
  4. Verify that "Shrink X" and "Shrink Y" are both set to 1.0

The UI writes directly to the same underlying ParamHolder instances used by the solver, ensuring consistency between visual configuration and batch processing.

Resolving Shrink/Strain Limit Conflicts

The scene builder validates strain limiting configurations in frontend/_scene_.py (lines 10005–10021) to prevent contradictory inputs. The solver treats shrink operations (non-unit shrink_x or shrink_y) as pre-scales of the rest shape, which conflicts with active strain constraints.

If you attempt to build a scene where shrink_x or shrink_y differs from 1.0 while strain_limit is greater than zero, the code invokes the Rust helper scene_shell_shrink_strain_limit_conflict and raises a ValueError with a descriptive message.

To resolve this, choose one of two approaches:

  • Approach A: Keep shrink/extend neutral (shrink_x = 1.0, shrink_y = 1.0) and use strain limiting to control deformation during simulation.
  • Approach B: Disable strain limiting (enable_strain_limit = False) if you require non-unit shrink values for pre-scaling the rest geometry.

Summary

  • Default behavior in blender_addon/models/groups.py disables strain limiting (enable_strain_limit = False) with a conservative 0.05 threshold.
  • Validation logic in frontend/_scene_.py prevents simultaneous use of strain limiting and non-unit shrink/extend values.
  • Configuration requires setting both the enable flag and limit value, then ensuring shrink_x and shrink_y equal 1.0.
  • API access is available through frontend._param_.PyParamHolder.set() for Python scripts and via Blender UI panels defined in blender_addon/ui/object_group.py.

Frequently Asked Questions

What is the default strain limit value in PPF Contact Solver?

The default value is 0.05 (5% stretch), defined in blender_addon/models/groups.py for all shell and rod object groups. However, the feature is disabled by default (enable_strain_limit = False), so you must explicitly enable it for the constraint to take effect.

Why do I get a ValueError when enabling strain limits with shrink parameters?

The validation code in frontend/_scene_.py (lines 10005–10021) raises a ValueError when it detects that shrink_x or shrink_y differs from 1.0 while strain_limit is greater than zero. This prevents the solver from receiving contradictory inputs, as shrink operations pre-scale the rest shape while strain limits constrain deformation relative to that rest shape.

Can I use strain limiting with pre-scaled or non-uniform meshes?

You cannot use strain limiting simultaneously with non-unit shrink values (shrink_x != 1.0 or shrink_y != 1.0). If your mesh requires pre-scaling, either apply the scale to the geometry before importing (baking it into the rest shape) and keep shrink values at 1.0, or disable strain limiting entirely by setting enable_strain_limit = False.

How does the solver enforce the strain limit during simulation?

Once configured, the Rust solver backend enforces the maximum allowable edge-length change each timestep, preventing any edge from stretching beyond the specified multiplicative factor relative to its rest-length. This occurs automatically during the scene.solve() call without requiring additional Python callbacks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →