1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
use crateReal;
use crate;
use crateShape;
/// 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 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
///
/// ```rust
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
/// use parry3d::shape::Ball;
/// use parry3d::math::{Pose, Vector};
///
/// 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(
/// Pose::translation(0.0, 0.0, 0.0), // start position
/// Vector::ZERO, // rotation center (local space)
/// Vector::new(2.0, 0.0, 0.0), // moving right at speed 2
/// Vector::new(0.0, 10.0, 0.0), // spinning around Y axis
/// );
///
/// // Ball 2: stationary at x=10
/// let motion2 = NonlinearRigidMotion::constant_position(
/// Pose::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
///
/// ```rust
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
/// use parry3d::shape::Cuboid;
/// use parry3d::math::{Pose, Vector};
///
/// // A cuboid tumbling through space
/// let cube = Cuboid::new(Vector::new(0.5, 0.5, 0.5));
///
/// // Large wall (very wide cuboid)
/// let wall = Cuboid::new(Vector::new(10.0, 10.0, 0.1));
///
/// // Cube falling and tumbling
/// let motion_cube = NonlinearRigidMotion::new(
/// Pose::translation(0.0, 5.0, 0.0), // starting 5 units above
/// Vector::ZERO, // rotate around center
/// Vector::new(0.0, -2.0, 0.0), // falling down
/// Vector::new(1.0, 0.5, 2.0), // tumbling (complex rotation)
/// );
///
/// // Wall is stationary
/// let motion_wall = NonlinearRigidMotion::constant_position(
/// Pose::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
///
/// ```rust
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::query::{cast_shapes_nonlinear, NonlinearRigidMotion};
/// use parry3d::shape::Ball;
/// use parry3d::math::{Pose, Vector};
///
/// 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(
/// Pose::translation(0.0, 0.0, 0.0),
/// Vector::ZERO,
/// Vector::new(-1.0, 0.0, 0.0), // moving AWAY from ball2
/// Vector::ZERO,
/// );
///
/// let motion2 = NonlinearRigidMotion::constant_position(
/// Pose::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
///
/// - [`NonlinearRigidMotion`] - Describes an object's complete motion
/// - [`cast_shapes`](crate::query::cast_shapes) - Linear shape casting (no rotation)
/// - [`ShapeCastHit`] - Result structure
/// - [`contact`](crate::query::contact::contact()) - Static contact queries (no motion)