//! §3.1, §3.2 and §2.7 *Render*: the frame range by division, and track evaluation. use wai_vector::clock::{frame_range, frames, in_range}; use wai_vector::track::eval; use wai_vector::wvg1::{Key, Table}; fn key(t: u32, value: i32, interp: u8) -> Key { Key { t, value, interp, zero: [0; 3] } } /// At `P = 2^40` (tb = 2^20, a rate of 1/2^20), `g·P` does not fit 64 bits for /// `g ≥ 2^23`, and wraps to 0 at `g = 2^24`. The range is decided by division. #[test] fn frame_range_by_division() { let p = 1u64 << 40; let (t0, dur) = (0u32, (1u32 << 31) - 1); assert_eq!(frame_range(t0, dur, p), (0, 0)); for g in [1u64, 1 << 23, 1 << 24, (1 << 24) + 1, (1 << 31) - 1, 1 << 32, 1 << 63, u64::MAX] { assert!(!in_range(g, t0, dur, p), "frame {g} is out of range at P = 2^40"); } assert!(in_range(0, t0, dur, p)); // An ordinary rate: 25 fps at tb = 120000, from t0 = 4801 for one second. let p = 4_800; // Its last tick is 124,800 = 26·4800. assert_eq!(frame_range(4_801, 120_000, p), (2, 26)); assert!(!in_range(1, 4_801, 120_000, p) && in_range(2, 4_801, 120_000, p) && in_range(26, 4_801, 120_000, p) && !in_range(27, 4_801, 120_000, p)); // A document with no frame at a rate: one tick between frame ticks. assert_eq!(frames(1, 1, 4_800).count(), 0); assert!(!in_range(0, 1, 1, 4_800)); } /// M2: a 750-frame roll at an integer speed, keys on frame ticks, lands every frame on /// the grid at every listed rate. #[test] fn integer_speed_roll_lands_on_the_grid() { let tb = 120_000u64; for (num, den) in [(25u64, 1u64), (30_000, 1_001), (60, 1), (24_000, 1_001)] { let p = den * tb / num; for k in [1i64, 3, -2, 7] { for frames_n in [750u64, 749, 1] { if frames_n * p >= 1 << 31 { continue; } let v0 = 12_345i64; let v1 = v0 + 256 * k * frames_n as i64; let keys = [key(0, v0 as i32, 1), key((frames_n * p) as u32, v1 as i32, 0)]; for m in 0..=frames_n { let v = eval(&keys, &[], (m * p) as i64); assert_eq!(v, v0 + 256 * k * m as i64, "{num}/{den}, k = {k}, {frames_n} frames, frame {m}"); } } } } } /// M5: every value lies between the two keys it interpolates, and each key's value is /// reached exactly, for linear keys and for tables that are not monotone. #[test] fn values_stay_between_their_keys() { let mut s = 0x2545_F491_4F6C_DD1Du64; let mut rnd = |n: u64| { s ^= s << 13; s ^= s >> 7; s ^= s << 17; s % n }; let mut tables = Vec::new(); for _ in 0..8 { let mut e = [0u32; 17]; e[16] = 65_536; for x in e.iter_mut().take(16).skip(1) { *x = rnd(65_537) as u32; } tables.push(Table { e }); } tables.push(Table { e: [0, 65536, 0, 65536, 0, 65536, 0, 65536, 0, 65536, 0, 65536, 0, 65536, 0, 65536, 65536] }); for case in 0..4_000 { let n = 2 + rnd(4) as usize; let mut t = rnd(1000) as u32; let mut keys = Vec::new(); for i in 0..n { let interp = if i + 1 == n { 0 } else { [0u8, 1, 2, 3, 10][rnd(5) as usize] }; let value = rnd(1 << 24) as i32 - (1 << 23); keys.push(key(t, value, interp)); t += 1 + if case % 3 == 0 { rnd(1 << 30) as u32 / n as u32 } else { rnd(5_000) as u32 }; } for (i, w) in keys.windows(2).enumerate() { let (a, b) = (w[0], w[1]); let (lo, hi) = (a.value.min(b.value) as i64, a.value.max(b.value) as i64); assert_eq!(eval(&keys, &tables, i64::from(a.t)), i64::from(a.value), "key {i} exact"); for tau in [a.t as i64, a.t as i64 + 1, (a.t as i64 + b.t as i64) / 2, b.t as i64 - 1] { let v = eval(&keys, &tables, tau); assert!(lo <= v && v <= hi, "case {case}: {v} outside [{lo}, {hi}] at {tau}"); } } let last = keys[n - 1]; assert_eq!(eval(&keys, &tables, i64::from(last.t)), i64::from(last.value)); assert_eq!(eval(&keys, &tables, i64::from(last.t) + 99), i64::from(last.value)); assert_eq!(eval(&keys, &tables, i64::from(keys[0].t) - 1), i64::from(keys[0].value)); } }