···395395396396type tca = { time : float; miss_distance : float; relative_velocity : float }
397397398398+let tca_at_index times pos1 pos2 vel1 vel2 i d2 =
399399+ let d = sqrt d2 in
400400+ let dv = Vec3.length (Vec3.sub vel1.(i) vel2.(i)) in
401401+ { time = times.(i); miss_distance = d; relative_velocity = dv }
402402+403403+let d2_between (pos1 : Vec3.t array) (pos2 : Vec3.t array) j =
404404+ let dx = pos1.(j).x -. pos2.(j).x in
405405+ let dy = pos1.(j).y -. pos2.(j).y in
406406+ let dz = pos1.(j).z -. pos2.(j).z in
407407+ (dx *. dx) +. (dy *. dy) +. (dz *. dz)
408408+398409let tca ~times ~pos1 ~pos2 ~vel1 ~vel2 =
399410 let n = Array.length times in
400411 if n < 3 then None
401412 else
402413 (* Step 1: find the minimum-distance index *)
414414+ (* Step 1: find the minimum-distance index *)
403415 let min_i = ref 0 in
404416 let min_d2 = ref Float.infinity in
405417 for i = 0 to n - 1 do
406406- let dx = pos1.(i).x -. pos2.(i).x in
407407- let dy = pos1.(i).y -. pos2.(i).y in
408408- let dz = pos1.(i).z -. pos2.(i).z in
409409- let d2 = (dx *. dx) +. (dy *. dy) +. (dz *. dz) in
418418+ let d2 = d2_between pos1 pos2 i in
410419 if d2 < !min_d2 then (
411420 min_d2 := d2;
412421 min_i := i)
413422 done;
414423 let i = !min_i in
415424 if i = 0 || i = n - 1 then
416416- (* Minimum at edge — cannot refine *)
417417- let d = sqrt !min_d2 in
418418- let dv = Vec3.length (Vec3.sub vel1.(i) vel2.(i)) in
419419- Some { time = times.(i); miss_distance = d; relative_velocity = dv }
425425+ Some (tca_at_index times pos1 pos2 vel1 vel2 i !min_d2)
420426 else
421421- (* Step 2: quadratic fit on d^2(t) using 3 points around minimum.
422422- d^2(t) = a*t^2 + b*t + c. Minimum at t* = -b/(2a). *)
427427+ (* Step 2: quadratic refinement around minimum *)
423428 let t0 = times.(i - 1) and t1 = times.(i) and t2 = times.(i + 1) in
424424- let d2_at j =
425425- let dx = pos1.(j).x -. pos2.(j).x in
426426- let dy = pos1.(j).y -. pos2.(j).y in
427427- let dz = pos1.(j).z -. pos2.(j).z in
428428- (dx *. dx) +. (dy *. dy) +. (dz *. dz)
429429- in
430430- let y0 = d2_at (i - 1) and y1 = d2_at i and y2 = d2_at (i + 1) in
431431- (* Quadratic fit: p(t) = a*(t-t1)^2 + b*(t-t1) + c
432432- where c = y1, and a,b from the two other points.
433433- Minimum at t1 - b/(2a). *)
429429+ let y0 = d2_between pos1 pos2 (i - 1) in
430430+ let y1 = d2_between pos1 pos2 i in
431431+ let y2 = d2_between pos1 pos2 (i + 1) in
434432 let h0 = t0 -. t1 and h2 = t2 -. t1 in
435433 let denom = h0 *. h2 *. (h2 -. h0) in
436436- if Float.abs denom < 1e-30 then
437437- let d = sqrt !min_d2 in
438438- let dv = Vec3.length (Vec3.sub vel1.(i) vel2.(i)) in
439439- Some { time = t1; miss_distance = d; relative_velocity = dv }
434434+ if Float.abs denom < 1e-30 || denom = 0.0 then
435435+ Some (tca_at_index times pos1 pos2 vel1 vel2 i !min_d2)
440436 else
441441- (* a = (h2*(y0-y1) - h0*(y2-y1)) / (h0*h2*(h2-h0)) *)
442437 let a = ((h2 *. (y0 -. y1)) -. (h0 *. (y2 -. y1))) /. denom in
443443- (* b = (h0*h0*(y2-y1) - h2*h2*(y0-y1)) / (h0*h2*(h2-h0)) *)
444438 let b =
445439 ((h0 *. h0 *. (y2 -. y1)) -. (h2 *. h2 *. (y0 -. y1))) /. denom
446440 in
447441 if a <= 0.0 then
448448- (* Concave — no minimum, use step-based *)
449449- let d = sqrt !min_d2 in
450450- let dv = Vec3.length (Vec3.sub vel1.(i) vel2.(i)) in
451451- Some { time = t1; miss_distance = d; relative_velocity = dv }
442442+ Some (tca_at_index times pos1 pos2 vel1 vel2 i !min_d2)
452443 else
453444 let dt = -.b /. (2.0 *. a) in
454454- let t_star = t1 +. dt in
455455- (* Clamp to [t0, t2] *)
456456- let t_star = Float.max t0 (Float.min t2 t_star) in
457457- let dt_clamped = t_star -. t1 in
458458- let d2_star =
459459- Float.max 0.0
460460- ((a *. dt_clamped *. dt_clamped) +. (b *. dt_clamped) +. y1)
461461- in
462462- let d_star = sqrt d2_star in
463463- (* Relative velocity: interpolate linearly *)
464464- let frac = (t_star -. t0) /. (t2 -. t0) in
465465- let frac = Float.max 0.0 (Float.min 1.0 frac) in
445445+ let t_star = Float.max t0 (Float.min t2 (t1 +. dt)) in
446446+ let dt_c = t_star -. t1 in
447447+ let d_star = sqrt (Float.max 0.0 ((a *. dt_c *. dt_c) +. (b *. dt_c) +. y1)) in
448448+ let frac = Float.max 0.0 (Float.min 1.0 ((t_star -. t0) /. (t2 -. t0))) in
466449 let dv_lo = Vec3.length (Vec3.sub vel1.(i - 1) vel2.(i - 1)) in
467450 let dv_hi = Vec3.length (Vec3.sub vel1.(i + 1) vel2.(i + 1)) in
468451 let dv = dv_lo +. (frac *. (dv_hi -. dv_lo)) in