pub fn cast_shapes_nonlinear(
motion1: &NonlinearRigidMotion,
g1: &dyn Shape,
motion2: &NonlinearRigidMotion,
g2: &dyn Shape,
start_time: f32,
end_time: f32,
stop_at_penetration: bool,
) -> Result<Option<ShapeCastHit>, Unsupported>Expand description
Computes when two shapes moving with translation and rotation will first collide.
This function performs nonlinear shape casting - finding the time of impact (TOI) for two shapes that are both translating and rotating. Unlike linear shape casting which only handles straight-line motion, this accounts for the curved trajectories created by rotation.
§What This Function Does
Given two shapes with complete rigid body motion (position, linear velocity, and angular velocity), this function finds:
- When they will first touch (time of impact)
- Where they touch (witness points)
- How they touch (contact normals)
The function solves a complex nonlinear root-finding problem to determine the exact moment when the shapes’ minimum distance reaches zero (or a target threshold).
§How It Differs from Linear Shape Casting
| Feature | Linear (cast_shapes) | Nonlinear (cast_shapes_nonlinear) |
|---|---|---|
| Motion type | Translation only | Translation + rotation |
| Trajectory | Straight line | Curved (helical path) |
| Input | Position + velocity | Full rigid motion (position + velocities + angular vel) |
| Use case | Sliding, sweeping | Spinning, tumbling, realistic physics |
| Performance | Faster | Slower (more complex) |
§Arguments
motion1- Complete motion description for shape 1 (NonlinearRigidMotion)- Starting position and orientation
- Linear velocity (translation)
- Angular velocity (rotation)
- Local center of rotation
g1- The first shape geometrymotion2- Complete motion description for shape 2g2- The second shape geometrystart_time- Beginning of time interval to check (typically0.0)end_time- End of time interval to check (e.g., your physics timestep)stop_at_penetration- Controls behavior when shapes start penetrating:true: Returns immediately withtime_of_impact = start_timefalse: Checks if they’re separating; if so, looks for later impacts
§The stop_at_penetration Parameter
This parameter is crucial for handling initially-penetrating shapes:
-
true(recommended for most cases): If shapes overlap atstart_time, immediately return a hit atstart_time. This is safer and prevents tunneling. -
false(advanced): If shapes overlap atstart_timeBUT are moving apart (separating velocity), ignore this initial penetration and search for a later collision that could cause tunneling. Use this when you have external penetration resolution and only care about future impacts.
§Returns
Ok(Some(hit))- Collision detected, seeShapeCastHitfor impact detailstime_of_impact: When collision occurs (in[start_time, end_time])witness1,witness2: Contact points on each shape (local space)normal1,normal2: Contact normals on each shape (local space)status: Algorithm convergence status
Ok(None)- No collision within the time intervalErr(Unsupported)- This shape pair is not supported for nonlinear casting
§Example: Basic Spinning Collision
use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
use parry3d::shape::Ball;
use nalgebra::{Isometry3, Point3, Vector3};
let ball1 = Ball::new(1.0);
let ball2 = Ball::new(1.0);
// Ball 1: moving right AND spinning around Y axis
let motion1 = NonlinearRigidMotion::new(
Isometry3::translation(0.0, 0.0, 0.0), // start position
Point3::origin(), // rotation center (local space)
Vector3::new(2.0, 0.0, 0.0), // moving right at speed 2
Vector3::new(0.0, 10.0, 0.0), // spinning around Y axis
);
// Ball 2: stationary at x=10
let motion2 = NonlinearRigidMotion::constant_position(
Isometry3::translation(10.0, 0.0, 0.0)
);
let result = cast_shapes_nonlinear(
&motion1, &ball1,
&motion2, &ball2,
0.0, // start at t=0
10.0, // check up to t=10
true, // stop if initially penetrating
);
if let Ok(Some(hit)) = result {
println!("Collision at time: {}", hit.time_of_impact);
println!("Contact point on ball1: {:?}", hit.witness1);
println!("Contact normal on ball1: {:?}", hit.normal1);
}§Example: Tumbling Box vs Stationary Wall
use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
use parry3d::shape::Cuboid;
use nalgebra::{Isometry3, Point3, Vector3};
// A cuboid tumbling through space
let cube = Cuboid::new(Vector3::new(0.5, 0.5, 0.5));
// Large wall (very wide cuboid)
let wall = Cuboid::new(Vector3::new(10.0, 10.0, 0.1));
// Cube falling and tumbling
let motion_cube = NonlinearRigidMotion::new(
Isometry3::translation(0.0, 5.0, 0.0), // starting 5 units above
Point3::origin(), // rotate around center
Vector3::new(0.0, -2.0, 0.0), // falling down
Vector3::new(1.0, 0.5, 2.0), // tumbling (complex rotation)
);
// Wall is stationary
let motion_wall = NonlinearRigidMotion::constant_position(
Isometry3::translation(0.0, 0.0, 0.0)
);
let result = cast_shapes_nonlinear(
&motion_cube, &cube,
&motion_wall, &wall,
0.0,
5.0, // check 5 seconds of motion
true,
);
if let Ok(Some(hit)) = result {
// Cube will hit wall while tumbling
// Time depends on both falling speed and rotation
println!("Tumbling cube hits wall at t = {}", hit.time_of_impact);
}§Example: Handling Initial Penetration
use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
use parry3d::shape::Ball;
use nalgebra::{Isometry3, Point3, Vector3};
let ball1 = Ball::new(2.0);
let ball2 = Ball::new(2.0);
// Balls overlapping (3 units apart, but radii sum to 4)
let motion1 = NonlinearRigidMotion::new(
Isometry3::translation(0.0, 0.0, 0.0),
Point3::origin(),
Vector3::new(-1.0, 0.0, 0.0), // moving AWAY from ball2
Vector3::zeros(),
);
let motion2 = NonlinearRigidMotion::constant_position(
Isometry3::translation(3.0, 0.0, 0.0)
);
// With stop_at_penetration = true
let result_stop = cast_shapes_nonlinear(
&motion1, &ball1, &motion2, &ball2,
0.0, 10.0, true, // STOP at penetration
);
// Returns Some(hit) with time_of_impact = 0.0
// With stop_at_penetration = false
let result_continue = cast_shapes_nonlinear(
&motion1, &ball1, &motion2, &ball2,
0.0, 10.0, false, // DON'T stop - they're separating
);
// Returns None - shapes are moving apart, no future impact§When to Use This vs Linear Shape Casting
Use cast_shapes_nonlinear when:
- Objects have significant angular velocity
- Rotation accuracy matters (spinning blades, tumbling debris)
- Physics simulation with full 6-DOF motion
- Objects can rotate > 10-15 degrees during timestep
Use cast_shapes (linear) when:
- Objects don’t rotate (angular velocity ≈ 0)
- Rotation is negligible for your timestep
- Performance is critical
- Implementing simple sweep/slide mechanics
§Performance Considerations
Nonlinear shape casting is significantly more expensive than linear:
- Iterative root-finding: Multiple evaluations to converge
- Rotational transforms: Matrix operations at each evaluation
- Complex geometry: Shape changes orientation during motion
Typical cost: 3-10x slower than linear shape casting, depending on:
- Shape complexity (balls faster than meshes)
- Angular velocity magnitude (faster rotation = more iterations)
- Time interval length (longer intervals may need more precision)
§Algorithm Notes
The implementation uses conservative root-finding algorithms that:
- Guarantee no false negatives (won’t miss collisions)
- May require multiple iterations to converge
- Handle degenerate cases (parallel surfaces, grazing contact)
- Account for numerical precision limits
§Common Pitfalls
-
Units: Ensure time, velocity, and angular velocity units match
- If time is in seconds, velocity should be units/second
- Angular velocity in radians/second (not degrees!)
-
Time interval: Keep
end_time - start_timereasonable- Very large intervals may miss fast rotations
- Typical: use your physics timestep (e.g., 1/60 second)
-
Rotation center:
local_centerinNonlinearRigidMotionmust be in the shape’s local coordinate system, not world space -
Initial penetration: Understand
stop_at_penetrationbehavior for your use case
§See Also
NonlinearRigidMotion- Describes an object’s complete motioncast_shapes- Linear shape casting (no rotation)ShapeCastHit- Result structurecontact- Static contact queries (no motion)