fourdgsPoseAt function

FourdgsPose? fourdgsPoseAt(
  1. FourdgsPoseSampled track,
  2. double t
)

The pose at scene time t, or null when the record has no samples.

Outside the sample range the pose is clamped, never extrapolated: before the first sample it is the first sample, at or after the last it is the last. Extrapolating produces a platform that accelerates away from the scene at the ends of the clip, which is never what the capture did.

Implementation

FourdgsPose? fourdgsPoseAt(FourdgsPoseSampled track, double t) {
  checkSceneTime(t);
  final n = track.sampleCount;
  if (n == 0) return null;
  if (t <= track.timeAt(0)) return _sample(track, 0);
  if (t >= track.timeAt(n - 1)) return _sample(track, n - 1);

  // Times are strictly increasing (enforced at parse), so a bisection is exact.
  var lo = 0;
  var hi = n - 1;
  while (hi - lo > 1) {
    final mid = (lo + hi) >> 1;
    if (track.timeAt(mid) <= t) {
      lo = mid;
    } else {
      hi = mid;
    }
  }

  if (track.interpolation == trajectoryStep) {
    return _sample(track, lo);
  }
  if (track.interpolation != trajectoryLinear) {
    // Unknown-but-legal, not malformed — but there is no defensible way to
    // invent the rule, and picking linear would silently answer a question the
    // file asked differently. Naming it is the whole obligation.
    throw FourdgsMalformedFile(
      "trajectory '${track.name}' uses interpolation ${track.interpolation}, "
      'which this build does not implement',
    );
  }

  final u = interpolationFraction(t, track.timeAt(lo), track.timeAt(lo + 1));
  final a = _sample(track, lo);
  final b = _sample(track, lo + 1);
  return FourdgsPose(
    rotation: fourdgsSlerp(a.rotation, b.rotation, u),
    translation: <double>[
      finiteLerp(a.translation[0], b.translation[0], u),
      finiteLerp(a.translation[1], b.translation[1], u),
      finiteLerp(a.translation[2], b.translation[2], u),
    ],
  );
}