Function cast_shapes_nonlinear

Source
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

FeatureLinear (cast_shapes)Nonlinear (cast_shapes_nonlinear)
Motion typeTranslation onlyTranslation + rotation
TrajectoryStraight lineCurved (helical path)
InputPosition + velocityFull rigid motion (position + velocities + angular vel)
Use caseSliding, sweepingSpinning, tumbling, realistic physics
PerformanceFasterSlower (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 geometry
  • motion2 - Complete motion description for shape 2
  • g2 - The second shape geometry
  • start_time - Beginning of time interval to check (typically 0.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 with time_of_impact = start_time
    • false: 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 at start_time, immediately return a hit at start_time. This is safer and prevents tunneling.

  • false (advanced): If shapes overlap at start_time BUT 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, see ShapeCastHit for impact details
    • time_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 interval
  • Err(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

  1. 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!)
  2. Time interval: Keep end_time - start_time reasonable

    • Very large intervals may miss fast rotations
    • Typical: use your physics timestep (e.g., 1/60 second)
  3. Rotation center: local_center in NonlinearRigidMotion must be in the shape’s local coordinate system, not world space

  4. Initial penetration: Understand stop_at_penetration behavior for your use case

§See Also