makepad/libs/tween/tests/golden/positions.rs
vjroger a8fb6c4374 widgets, libs: a tween engine, the theme lab, the designer and the surface material, on the family crates
The widget half of the widget catalogue's later work, ported onto
makepad-widgets-core and the family crates.

- libs/tween: a dependency-free tween and timeline engine -- nested
  timelines with positions and labels, stagger, repeat and yoyo, seek and
  reverse, time scale, a ticker with lag smoothing, a global pause and
  reduced motion, colour-space interpolation, keyframes and motion paths.
  The core binds it to the widget tree (tween, a widget's TweenHost),
  gives script a mod.tween (tween_script) and a live inspector
  (tween_inspect, tween_inspector). The Animator's Ease now answers from
  the engine's easing, so the curves have one source; the Animator keeps
  its states, its keyframe timelines and its own clock.
- libs/gizmo and a Gizmo3d / ViewCube widget.
- core: the ease editor, the sequencer, diagonal headings, hotkeys, the
  hotkey editor and keyboard map, the overlay layer host, a conceding row
  and the width a widget would take without drawing; a theme mix and a
  person's own theme edits that survive a style reload (theme_mix,
  set_theme_edit), both taken off by set_base_theme.
- the surface material: buttons, check boxes, toggles, sliders and knobs
  carry the moulded material behind one uniform, a press idiom per sign
  of material_press, and four sheets that raise it: neumorphic, molded,
  glossy and milled.
- the sheets: a row of fields is one height under every sheet. A sheet
  line that names a widget the app did not register (a family left out)
  is skipped when the sheet installs, so the date fields a sheet sizes
  need the dates family only where it is linked.
- extras: the gradient editor, registered with the colour picker it
  builds on.
- tweaker: the designer (a widget's literal located and edited in its
  source as hunks), the theme lab and the theme builder. The window reads
  the docked panel's band through the tweaker's hooks.
- the panel no longer wears the app's theme (the design surface keeps
  its own palette), so the worn-theme tests go with it.

Five theme builder tests fail here as they do on the branch this comes
from (every_built_theme_reads and four more on text contrast);
radial_menu's tab_under_an_open_menu_finds_nowhere_to_go fails here as it
does on dev.
2026-09-29 01:55:12 +02:00

839 lines
80 KiB
Rust

// GENERATED by gsap/emit_rust.js from gsap/out/positions.json -- do not edit by hand.
// GSAP 3.15.0 under Node v22.22.2; generator golden.js; root time after the frame tick: 0.
// Numbers are the golden doubles (shortest round-trip repr, bit-exact). Tolerances are the test's business
// (see design/gsap_reconciliation.md D: GSAP rounds written property values to 1e-6, so value columns need 5e-7).
// Each case: children after tl.duration() (sorted by start), start times at add, labels, duration; `extra` flattened.
#![allow(
dead_code,
unused_imports,
clippy::approx_constant,
clippy::excessive_precision,
clippy::unreadable_literal
)]
use super::common::*;
pub const GSAP_VERSION: &str = "3.15.0";
pub const NOTES: &[&str] = &["Every case builds tl = gsap.timeline({paused:true}); children are tweens on fresh plain objects {x:0} with vars {x:1, duration:d} unless stated.", "children_in_timeline_order = tl.getChildren(false,true,true) (sorted by startTime); startTime_at_add = child.startTime() read immediately after the add call (before later adds could shift anything).", "labels = tl.labels after all adds; labels_after_duration_call = same after tl.duration() (which can shift negative starts)."];
#[derive(Clone, Copy, Debug)]
pub struct Child {
pub name: &'static str,
pub kind: &'static str,
pub data: Option<&'static str>,
pub start_time: f64,
pub duration: f64,
pub total_duration: f64,
pub end_time: f64,
pub end_time_no_repeats: f64,
pub time_scale: f64,
pub delay: f64,
pub repeat: f64,
}
#[derive(Clone, Copy, Debug)]
pub struct PositionCase {
pub id: &'static str,
pub description: &'static str,
pub gsap_calls: &'static [&'static str],
pub children: &'static [Child],
pub insertion_order: &'static [&'static str],
pub start_time_at_add: &'static [(&'static str, f64)],
pub start_times_before_duration_call: &'static [(&'static str, f64)],
pub recent: Option<&'static str>,
pub labels: &'static [(&'static str, f64)],
pub labels_before_duration_call: &'static [(&'static str, f64)],
pub duration: f64,
pub total_duration: f64,
pub child_count: u32,
pub timeline_start_time_in_parent: f64,
pub extra: Pairs,
}
pub const CASES: &[PositionCase] = &[
PositionCase { id: "seq_defaults", description: "three tweens with no position parameter",
gsap_calls: &["tl = gsap.timeline({paused:true})", "tl.to({x:0},{x:1,duration:1}) // A", "tl.to({x:0},{x:1,duration:0.5}) // B", "tl.to({x:0},{x:1,duration:2}) // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 0.5, total_duration: 0.5, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 1.5, duration: 2.0, total_duration: 2.0, end_time: 3.5, end_time_no_repeats: 3.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 1.0), ("C", 1.5)],
start_times_before_duration_call: &[("A", 0.0), ("B", 1.0), ("C", 1.5)],
labels: &[], labels_before_duration_call: &[],
duration: 3.5, total_duration: 3.5, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "absolute_out_of_order", description: "absolute positions 0, 2, 1",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "tl.to(o,{duration:1},2) // B", "tl.to(o,{duration:1},1) // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 2.0), ("C", 1.0)],
start_times_before_duration_call: &[("A", 0.0), ("C", 1.0), ("B", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 3.0, total_duration: 3.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "relative_plus_minus", description: "\"+=0.5\" then \"-=0.25\" after a 1 s tween",
gsap_calls: &["tl.to(o,{duration:1}) // A", "tl.to(o,{duration:1},\"+=0.5\") // B", "tl.to(o,{duration:1},\"-=0.25\") // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.5, duration: 1.0, total_duration: 1.0, end_time: 2.5, end_time_no_repeats: 2.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 2.25, duration: 1.0, total_duration: 1.0, end_time: 3.25, end_time_no_repeats: 3.25, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 1.5), ("C", 2.25)],
start_times_before_duration_call: &[("A", 0.0), ("B", 1.5), ("C", 2.25)],
labels: &[], labels_before_duration_call: &[],
duration: 3.25, total_duration: 3.25, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "relative_plus_first_child", description: "\"+=0.5\" as the very first child of an empty timeline",
gsap_calls: &["tl.to(o,{duration:1},\"+=0.5\") // A"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.5, duration: 1.0, total_duration: 1.0, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", 0.5)],
start_times_before_duration_call: &[("A", 0.5)],
labels: &[], labels_before_duration_call: &[],
duration: 1.5, total_duration: 1.5, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "relative_plus_is_timeline_end_not_previous", description: "\"+=\" is relative to the END OF THE TIMELINE, not the previous child",
gsap_calls: &["tl.to(o,{duration:3},0) // A", "tl.to(o,{duration:1},0) // B", "tl.to(o,{duration:1},\"+=0.5\") // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 3.0, total_duration: 3.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 3.5, duration: 1.0, total_duration: 1.0, end_time: 4.5, end_time_no_repeats: 4.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 0.0), ("C", 3.5)],
start_times_before_duration_call: &[("A", 0.0), ("B", 0.0), ("C", 3.5)],
labels: &[], labels_before_duration_call: &[],
duration: 4.5, total_duration: 4.5, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "default_position_is_timeline_end", description: "no position = end of timeline (not end of previous child)",
gsap_calls: &["tl.to(o,{duration:3},0) // A", "tl.to(o,{duration:1},0) // B", "tl.to(o,{duration:1}) // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 3.0, total_duration: 3.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 3.0, duration: 1.0, total_duration: 1.0, end_time: 4.0, end_time_no_repeats: 4.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 0.0), ("C", 3.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 0.0), ("C", 3.0)],
labels: &[], labels_before_duration_call: &[],
duration: 4.0, total_duration: 4.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "anchor_lt", description: "position \"<\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"<\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 1.0, duration: 4.0, total_duration: 4.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 1.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 1.0)],
labels: &[], labels_before_duration_call: &[],
duration: 5.0, total_duration: 5.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("<")), ("X_startTime", Val::F(1.0))] },
PositionCase { id: "anchor_lt0_25", description: "position \"<0.25\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"<0.25\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 1.25, duration: 4.0, total_duration: 4.0, end_time: 5.25, end_time_no_repeats: 5.25, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 1.25)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 1.25)],
labels: &[], labels_before_duration_call: &[],
duration: 5.25, total_duration: 5.25, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("<0.25")), ("X_startTime", Val::F(1.25))] },
PositionCase { id: "anchor_ltneg_0_1", description: "position \"<-0.1\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"<-0.1\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 0.9, duration: 4.0, total_duration: 4.0, end_time: 4.9, end_time_no_repeats: 4.9, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 0.9)],
start_times_before_duration_call: &[("Q", 0.0), ("X", 0.9), ("P", 1.0)],
labels: &[], labels_before_duration_call: &[],
duration: 5.0, total_duration: 5.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("<-0.1")), ("X_startTime", Val::F(0.9))] },
PositionCase { id: "anchor_gt", description: "position \">\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 3.0, duration: 4.0, total_duration: 4.0, end_time: 7.0, end_time_no_repeats: 7.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 3.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 3.0)],
labels: &[], labels_before_duration_call: &[],
duration: 7.0, total_duration: 7.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">")), ("X_startTime", Val::F(3.0))] },
PositionCase { id: "anchor_gt0_3", description: "position \">0.3\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">0.3\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 3.3, duration: 4.0, total_duration: 4.0, end_time: 7.3, end_time_no_repeats: 7.3, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 3.3)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 3.3)],
labels: &[], labels_before_duration_call: &[],
duration: 7.3, total_duration: 7.3, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">0.3")), ("X_startTime", Val::F(3.3))] },
PositionCase { id: "anchor_gtneg_0_2", description: "position \">-0.2\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">-0.2\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 2.8, duration: 4.0, total_duration: 4.0, end_time: 6.8, end_time_no_repeats: 6.8, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 2.8)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 2.8)],
labels: &[], labels_before_duration_call: &[],
duration: 6.8, total_duration: 6.8, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">-0.2")), ("X_startTime", Val::F(2.8))] },
PositionCase { id: "anchor_ltplus_25pct", description: "position \"<+=25%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"<+=25%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 2.0, duration: 4.0, total_duration: 4.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 2.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("<+=25%")), ("X_startTime", Val::F(2.0))] },
PositionCase { id: "anchor_gtminus_50pct", description: "position \">-=50%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">-=50%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 1.0, duration: 4.0, total_duration: 4.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 1.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 1.0)],
labels: &[], labels_before_duration_call: &[],
duration: 5.0, total_duration: 5.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">-=50%")), ("X_startTime", Val::F(1.0))] },
PositionCase { id: "anchor_lt25pct", description: "position \"<25%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"<25%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 1.5, duration: 4.0, total_duration: 4.0, end_time: 5.5, end_time_no_repeats: 5.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 1.5)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 1.5)],
labels: &[], labels_before_duration_call: &[],
duration: 5.5, total_duration: 5.5, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("<25%")), ("X_startTime", Val::F(1.5))] },
PositionCase { id: "anchor_gt50pct", description: "position \">50%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">50%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 4.0, duration: 4.0, total_duration: 4.0, end_time: 8.0, end_time_no_repeats: 8.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 4.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 4.0)],
labels: &[], labels_before_duration_call: &[],
duration: 8.0, total_duration: 8.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">50%")), ("X_startTime", Val::F(4.0))] },
PositionCase { id: "anchor_gtneg_50pct", description: "position \">-50%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\">-50%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 2.0, duration: 4.0, total_duration: 4.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 2.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S(">-50%")), ("X_startTime", Val::F(2.0))] },
PositionCase { id: "anchor_plus_25pct", description: "position \"+=25%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"+=25%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 6.0, duration: 4.0, total_duration: 4.0, end_time: 10.0, end_time_no_repeats: 10.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 6.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 6.0)],
labels: &[], labels_before_duration_call: &[],
duration: 10.0, total_duration: 10.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("+=25%")), ("X_startTime", Val::F(6.0))] },
PositionCase { id: "anchor_minus_50pct", description: "position \"-=50%\". Q spans 0..5 (so timeline end=5), previous child P spans 1..3 (duration 2), inserted X has duration 4 (so %-of-P and %-of-X differ)",
gsap_calls: &["tl.to(o,{duration:5},0) // Q", "tl.to(o,{duration:2},1) // P (previous / most recently added)", "tl.to(o,{duration:4},\"-=50%\") // X"],
children: &[
Child { name: "Q", kind: "tween", data: None, start_time: 0.0, duration: 5.0, total_duration: 5.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "P", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "X", kind: "tween", data: None, start_time: 3.0, duration: 4.0, total_duration: 4.0, end_time: 7.0, end_time_no_repeats: 7.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["Q", "P", "X"], recent: Some("X"),
start_time_at_add: &[("Q", 0.0), ("P", 1.0), ("X", 3.0)],
start_times_before_duration_call: &[("Q", 0.0), ("P", 1.0), ("X", 3.0)],
labels: &[], labels_before_duration_call: &[],
duration: 7.0, total_duration: 7.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[("position", Val::S("-=50%")), ("X_startTime", Val::F(3.0))] },
PositionCase { id: "labels_cumulative", description: "label arithmetic; each child added in sequence to the same timeline",
gsap_calls: &["tl.to(o,{duration:2},0) // A", "tl.addLabel(\"intro\", 1)", "tl.to(o,{duration:1},\"intro\") // B", "tl.to(o,{duration:1},\"intro+=0.2\") // C", "tl.to(o,{duration:1},\"intro-=0.5\") // D", "tl.to(o,{duration:1},\"intro+=50%\") // E", "tl.to(o,{duration:1},\"missing\") // F"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 2.0, total_duration: 2.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 0.5, duration: 1.0, total_duration: 1.0, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 1.2, duration: 1.0, total_duration: 1.0, end_time: 2.2, end_time_no_repeats: 2.2, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "E", kind: "tween", data: None, start_time: 1.5, duration: 1.0, total_duration: 1.0, end_time: 2.5, end_time_no_repeats: 2.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "F", kind: "tween", data: None, start_time: 2.5, duration: 1.0, total_duration: 1.0, end_time: 3.5, end_time_no_repeats: 3.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C", "D", "E", "F"], recent: Some("F"),
start_time_at_add: &[("A", 0.0), ("B", 1.0), ("C", 1.2), ("D", 0.5), ("E", 1.5), ("F", 2.5)],
start_times_before_duration_call: &[("A", 0.0), ("D", 0.5), ("B", 1.0), ("C", 1.2), ("E", 1.5), ("F", 2.5)],
labels: &[("intro", 1.0), ("missing", 2.5)], labels_before_duration_call: &[("intro", 1.0), ("missing", 2.5)],
duration: 3.5, total_duration: 3.5, child_count: 6, timeline_start_time_in_parent: 0.0,
extra: &[
("labels_before_missing.intro", Val::F(1.0)), ("timeline_duration_before_missing", Val::F(2.5)), ("missing_label_created", Val::B(true)),
("missing_label_value", Val::F(2.5)),
] },
PositionCase { id: "label_missing_with_offset", description: "unknown label with an offset: \"later+=1\"",
gsap_calls: &["tl.to(o,{duration:2},0) // A", "tl.to(o,{duration:1},\"later+=1\") // B"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 2.0, total_duration: 2.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 3.0, duration: 1.0, total_duration: 1.0, end_time: 4.0, end_time_no_repeats: 4.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 3.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 3.0)],
labels: &[("later", 2.0)], labels_before_duration_call: &[("later", 2.0)],
duration: 4.0, total_duration: 4.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "label_isolated_intro_plus_50pct", description: "isolated: \"intro+=50%\" where the inserted child has duration 4 (percent is of the INSERTED child)",
gsap_calls: &["tl.to(o,{duration:2},0) // A", "tl.addLabel(\"intro\",1)", "tl.to(o,{duration:4},\"intro+=50%\") // B"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 2.0, total_duration: 2.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 3.0, duration: 4.0, total_duration: 4.0, end_time: 7.0, end_time_no_repeats: 7.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 3.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 3.0)],
labels: &[("intro", 1.0)], labels_before_duration_call: &[("intro", 1.0)],
duration: 7.0, total_duration: 7.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "label_numeric_string", description: "a numeric string is an absolute time unless a label with that exact name exists",
gsap_calls: &["tl.to(o,{duration:1},\"2\") // A", "tl.addLabel(\"3\", 0.5)", "tl.to(o,{duration:1},\"3\") // B"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 0.5, duration: 1.0, total_duration: 1.0, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 2.0), ("B", 0.5)],
start_times_before_duration_call: &[("B", 0.5), ("A", 2.0)],
labels: &[("3", 0.5)], labels_before_duration_call: &[("3", 0.5)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "addLabel_no_position", description: "addLabel with no position = end of timeline",
gsap_calls: &["tl.to(o,{duration:1.5})", "tl.addLabel(\"end\")"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.5, total_duration: 1.5, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", 0.0)],
start_times_before_duration_call: &[("A", 0.0)],
labels: &[("end", 1.5)], labels_before_duration_call: &[("end", 1.5)],
duration: 1.5, total_duration: 1.5, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "previous_lt_is_last_added", description: "\"<\" anchors to the most recently ADDED child (B at 1), not the one with the latest start (A at 5)",
gsap_calls: &["tl.to(o,{duration:1},5) // A", "tl.to(o,{duration:1},1) // B", "tl.to(o,{duration:1},\"<\") // C"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 5.0, duration: 1.0, total_duration: 1.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 5.0), ("B", 1.0), ("C", 1.0)],
start_times_before_duration_call: &[("B", 1.0), ("C", 1.0), ("A", 5.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "previous_gt_is_last_added", description: "\">\" anchors to the END of the most recently ADDED child",
gsap_calls: &["tl.to(o,{duration:1},5) // A", "tl.to(o,{duration:1},1) // B", "tl.to(o,{duration:1},\">\") // D"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 5.0, duration: 1.0, total_duration: 1.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "D"], recent: Some("D"),
start_time_at_add: &[("A", 5.0), ("B", 1.0), ("D", 2.0)],
start_times_before_duration_call: &[("B", 1.0), ("D", 2.0), ("A", 5.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "previous_chain", description: "cumulative: C at \"<\" becomes the new previous, so D at \">\" anchors to C",
gsap_calls: &["tl.to(o,{duration:1},5) // A", "tl.to(o,{duration:1},1) // B", "tl.to(o,{duration:1},\"<\") // C", "tl.to(o,{duration:1},\">\") // D"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 5.0, duration: 1.0, total_duration: 1.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C", "D"], recent: Some("D"),
start_time_at_add: &[("A", 5.0), ("B", 1.0), ("C", 1.0), ("D", 2.0)],
start_times_before_duration_call: &[("B", 1.0), ("C", 1.0), ("D", 2.0), ("A", 5.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 4, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "negative_start_first_child", description: "child at -0.5 as the only child of a PAUSED top-level timeline (note timeline_startTime_in_parent: GSAP divides the shift by _ts which is 0 while paused -> -Infinity)",
gsap_calls: &["tl = gsap.timeline({paused:true})", "tl.to(o,{duration:1},-0.5) // A", "tl.duration()"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", -0.5)],
start_times_before_duration_call: &[("A", -0.5)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 1.0, child_count: 1, timeline_start_time_in_parent: f64::NEG_INFINITY,
extra: &[] },
PositionCase { id: "negative_start_first_child_unpaused_root", description: "same but the timeline is NOT paused (child of the root, which has smoothChildTiming): children shift +0.5 and the timeline startTime moves -0.5",
gsap_calls: &["tl = gsap.timeline() // not paused, root never advances here", "tl.to(o,{duration:1},-0.5) // A", "tl.duration()"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", -0.5)],
start_times_before_duration_call: &[("A", -0.5)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 1.0, child_count: 1, timeline_start_time_in_parent: -0.5,
extra: &[("timeline_time_after", Val::F(0.5))] },
PositionCase { id: "negative_start_nested_in_paused_parent", description: "inner timeline (inside a paused outer, smoothChildTiming=false) with a child at -0.5: children shift, inner startTime does NOT move",
gsap_calls: &["outer = gsap.timeline({paused:true})", "inner = gsap.timeline(); outer.add(inner, 1)", "inner.to(o,{duration:1},-0.5) // A", "inner.duration()"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", -0.5)],
start_times_before_duration_call: &[("A", -0.5)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 1.0, child_count: 1, timeline_start_time_in_parent: 1.0,
extra: &[("outer_duration", Val::F(2.0))] },
PositionCase { id: "negative_start_then_more", description: "label at 0.25, child at -0.5, then a child with no position (the parse of B triggers the shift)",
gsap_calls: &["tl.addLabel(\"L\", 0.25)", "tl.to(o,{duration:1},-0.5) // A", "tl.to(o,{duration:1}) // B", "tl.to(o,{duration:1},\"L\") // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 0.25, duration: 1.0, total_duration: 1.0, end_time: 1.25, end_time_no_repeats: 1.25, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", -0.5), ("B", 1.0), ("C", 0.25)],
start_times_before_duration_call: &[("A", 0.0), ("C", 0.25), ("B", 1.0)],
labels: &[("L", 0.25)], labels_before_duration_call: &[("L", 0.25)],
duration: 2.0, total_duration: 2.0, child_count: 3, timeline_start_time_in_parent: f64::NEG_INFINITY,
extra: &[] },
PositionCase { id: "nested_inner_repeat1", description: "inner timeline (2 x 1 s, repeat:1) added at \"+=1\"",
gsap_calls: &["tl.to(o,{duration:1}) // A", "inner = gsap.timeline({repeat:1}); inner.to(o,{duration:1}).to(o,{duration:1})", "tl.add(inner, \"+=1\")"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "inner", kind: "timeline", data: None, start_time: 2.0, duration: 2.0, total_duration: 4.0, end_time: 6.0, end_time_no_repeats: 4.0, time_scale: 1.0, delay: 0.0, repeat: 1.0 },
],
insertion_order: &["A", "inner"], recent: Some("inner"),
start_time_at_add: &[("A", 0.0), ("inner", 2.0)],
start_times_before_duration_call: &[("A", 0.0), ("inner", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("inner_startTime", Val::F(2.0)), ("inner_duration", Val::F(2.0)), ("inner_totalDuration", Val::F(4.0)), ("inner_endTime", Val::F(6.0)),
("inner_timeScale", Val::F(1.0)), ("outer_duration", Val::F(6.0)),
] },
PositionCase { id: "nested_inner_repeat1_timescale2_before_add", description: "same, inner.timeScale(2) BEFORE add",
gsap_calls: &["tl.to(o,{duration:1}) // A", "inner = gsap.timeline({repeat:1}); inner.to(o,{duration:1}).to(o,{duration:1})", "inner.timeScale(2)", "tl.add(inner, \"+=1\")"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "inner", kind: "timeline", data: None, start_time: 2.0, duration: 2.0, total_duration: 4.0, end_time: 4.0, end_time_no_repeats: 3.0, time_scale: 2.0, delay: 0.0, repeat: 1.0 },
],
insertion_order: &["A", "inner"], recent: Some("inner"),
start_time_at_add: &[("A", 0.0), ("inner", 2.0)],
start_times_before_duration_call: &[("A", 0.0), ("inner", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 4.0, total_duration: 4.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("inner_startTime", Val::F(2.0)), ("inner_duration", Val::F(2.0)), ("inner_totalDuration", Val::F(4.0)), ("inner_endTime", Val::F(4.0)),
("inner_timeScale", Val::F(2.0)), ("outer_duration", Val::F(4.0)),
] },
PositionCase { id: "nested_inner_repeat1_timescale2_after_add", description: "same, inner.timeScale(2) AFTER add (outer.smoothChildTiming=false so startTime stays)",
gsap_calls: &["tl.to(o,{duration:1}) // A", "inner = gsap.timeline({repeat:1}); inner.to(o,{duration:1}).to(o,{duration:1})", "tl.add(inner, \"+=1\")", "inner.timeScale(2)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "inner", kind: "timeline", data: None, start_time: 2.0, duration: 2.0, total_duration: 4.0, end_time: 4.0, end_time_no_repeats: 3.0, time_scale: 2.0, delay: 0.0, repeat: 1.0 },
],
insertion_order: &["A", "inner"], recent: Some("inner"),
start_time_at_add: &[("A", 0.0), ("inner", 2.0)],
start_times_before_duration_call: &[("A", 0.0), ("inner", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 4.0, total_duration: 4.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("inner_startTime", Val::F(2.0)), ("inner_duration", Val::F(2.0)), ("inner_totalDuration", Val::F(4.0)), ("inner_endTime", Val::F(4.0)),
("inner_timeScale", Val::F(2.0)), ("outer_duration", Val::F(4.0)),
] },
PositionCase { id: "gt_after_repeat2", description: "\">\" after a child with repeat:2 (1 s) -> includes repeats",
gsap_calls: &["tl.to(o,{duration:1,repeat:2}) // A", "tl.to(o,{duration:1},\">\") // B", "tl.to(o,{duration:1},\"<\") // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 3.0, end_time: 3.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 2.0 },
Child { name: "B", kind: "tween", data: None, start_time: 3.0, duration: 1.0, total_duration: 1.0, end_time: 4.0, end_time_no_repeats: 4.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 3.0, duration: 1.0, total_duration: 1.0, end_time: 4.0, end_time_no_repeats: 4.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 3.0), ("C", 3.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 3.0), ("C", 3.0)],
labels: &[], labels_before_duration_call: &[],
duration: 4.0, total_duration: 4.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "gt_after_repeat2_repeatDelay", description: "\">\" after repeat:2 repeatDelay:0.5 (1 s) -> totalDuration 4",
gsap_calls: &["tl.to(o,{duration:1,repeat:2,repeatDelay:0.5}) // A", "tl.to(o,{duration:1},\">\") // B"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 4.0, end_time: 4.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 2.0 },
Child { name: "B", kind: "tween", data: None, start_time: 4.0, duration: 1.0, total_duration: 1.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 4.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 4.0)],
labels: &[], labels_before_duration_call: &[],
duration: 5.0, total_duration: 5.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "gt_after_repeat_infinite", description: "\">\" after repeat:-1 -> uses ONE iteration; later default positions are clipped to the recent child end",
gsap_calls: &["tl.to(o,{duration:1,repeat:-1}) // A", "tl.to(o,{duration:1},\">\") // B", "tl.to(o,{duration:1}) // C", "tl.to(o,{duration:1},\"+=0.5\") // D"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 10000000000.0, end_time: 10000000000.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: -1.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 3.5, duration: 1.0, total_duration: 1.0, end_time: 4.5, end_time_no_repeats: 4.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C", "D"], recent: Some("D"),
start_time_at_add: &[("A", 0.0), ("B", 1.0), ("C", 2.0), ("D", 3.5)],
start_times_before_duration_call: &[("A", 0.0), ("B", 1.0), ("C", 2.0), ("D", 3.5)],
labels: &[], labels_before_duration_call: &[],
duration: 10000000000.0, total_duration: 10000000000.0, child_count: 4, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "call_plus_1", description: "tl.call(fn, null, \"+=1\") after a 1 s tween",
gsap_calls: &["tl.to(o,{duration:1}) // A", "tl.call(fn, null, \"+=1\") // call"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "call", kind: "tween", data: None, start_time: 2.0, duration: 0.0, total_duration: 0.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "call"], recent: Some("call"),
start_time_at_add: &[("A", 0.0), ("call", 2.0)],
start_times_before_duration_call: &[("A", 0.0), ("call", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 2.0, total_duration: 2.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "set_at_2", description: "tl.set(obj,{x:1},2) on an empty timeline",
gsap_calls: &["tl.set(o,{x:1},2) // S"],
children: &[
Child { name: "S", kind: "tween", data: None, start_time: 2.0, duration: 0.0, total_duration: 0.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["S"], recent: Some("S"),
start_time_at_add: &[("S", 2.0)],
start_times_before_duration_call: &[("S", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 2.0, total_duration: 2.0, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "zero_duration_at_lt", description: "zero-duration tween at \"<\" after a child at 1..3",
gsap_calls: &["tl.to(o,{duration:2},1) // A", "tl.to(o,{x:1,duration:0},\"<\") // Z"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "Z", kind: "tween", data: None, start_time: 1.0, duration: 0.0, total_duration: 0.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "Z"], recent: Some("Z"),
start_time_at_add: &[("A", 1.0), ("Z", 1.0)],
start_times_before_duration_call: &[("A", 1.0), ("Z", 1.0)],
labels: &[], labels_before_duration_call: &[],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "timeline_repeat2_rdelay05_yoyo", description: "timeline {repeat:2, repeatDelay:0.5, yoyo:true} with one 1 s tween (ease none, x 0->100); sampled with tl.totalTime(tt) in increasing order",
gsap_calls: &["tl = gsap.timeline({paused:true, repeat:2, repeatDelay:0.5, yoyo:true})", "tl.to(o,{x:100,duration:1,ease:\"none\"})", "for tt of samples: tl.totalTime(tt); read tl.iteration(), tl.time(), tl.progress(), o.x"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", 0.0)],
start_times_before_duration_call: &[("A", 0.0)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 4.0, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[
("samples.len", Val::F(13.0)), ("samples.0.totalTime_set", Val::F(0.0)), ("samples.0.iteration", Val::F(1.0)), ("samples.0.time", Val::F(0.0)),
("samples.0.totalTime", Val::F(0.0)), ("samples.0.progress", Val::F(0.0)), ("samples.0.totalProgress", Val::F(0.0)), ("samples.0.x", Val::F(0.0)),
("samples.1.totalTime_set", Val::F(0.5)), ("samples.1.iteration", Val::F(1.0)), ("samples.1.time", Val::F(0.5)), ("samples.1.totalTime", Val::F(0.5)),
("samples.1.progress", Val::F(0.5)), ("samples.1.totalProgress", Val::F(0.125)), ("samples.1.x", Val::F(50.0)),
("samples.2.totalTime_set", Val::F(1.0)), ("samples.2.iteration", Val::F(1.0)), ("samples.2.time", Val::F(1.0)), ("samples.2.totalTime", Val::F(1.0)),
("samples.2.progress", Val::F(1.0)), ("samples.2.totalProgress", Val::F(0.25)), ("samples.2.x", Val::F(100.0)),
("samples.3.totalTime_set", Val::F(1.25)), ("samples.3.iteration", Val::F(1.0)), ("samples.3.time", Val::F(1.0)), ("samples.3.totalTime", Val::F(1.25)),
("samples.3.progress", Val::F(1.0)), ("samples.3.totalProgress", Val::F(0.3125)), ("samples.3.x", Val::F(100.0)),
("samples.4.totalTime_set", Val::F(1.5)), ("samples.4.iteration", Val::F(1.0)), ("samples.4.time", Val::F(1.0)), ("samples.4.totalTime", Val::F(1.5)),
("samples.4.progress", Val::F(1.0)), ("samples.4.totalProgress", Val::F(0.375)), ("samples.4.x", Val::F(100.0)),
("samples.5.totalTime_set", Val::F(2.0)), ("samples.5.iteration", Val::F(2.0)), ("samples.5.time", Val::F(0.5)), ("samples.5.totalTime", Val::F(2.0)),
("samples.5.progress", Val::F(0.5)), ("samples.5.totalProgress", Val::F(0.5)), ("samples.5.x", Val::F(50.0)), ("samples.6.totalTime_set", Val::F(2.5)),
("samples.6.iteration", Val::F(2.0)), ("samples.6.time", Val::F(0.0)), ("samples.6.totalTime", Val::F(2.5)), ("samples.6.progress", Val::F(0.0)),
("samples.6.totalProgress", Val::F(0.625)), ("samples.6.x", Val::F(0.0)), ("samples.7.totalTime_set", Val::F(2.75)),
("samples.7.iteration", Val::F(2.0)), ("samples.7.time", Val::F(0.0)), ("samples.7.totalTime", Val::F(2.75)), ("samples.7.progress", Val::F(0.0)),
("samples.7.totalProgress", Val::F(0.6875)), ("samples.7.x", Val::F(0.0)), ("samples.8.totalTime_set", Val::F(3.0)),
("samples.8.iteration", Val::F(2.0)), ("samples.8.time", Val::F(0.0)), ("samples.8.totalTime", Val::F(3.0)), ("samples.8.progress", Val::F(0.0)),
("samples.8.totalProgress", Val::F(0.75)), ("samples.8.x", Val::F(0.0)), ("samples.9.totalTime_set", Val::F(3.5)), ("samples.9.iteration", Val::F(3.0)),
("samples.9.time", Val::F(0.5)), ("samples.9.totalTime", Val::F(3.5)), ("samples.9.progress", Val::F(0.5)), ("samples.9.totalProgress", Val::F(0.875)),
("samples.9.x", Val::F(50.0)), ("samples.10.totalTime_set", Val::F(3.75)), ("samples.10.iteration", Val::F(3.0)), ("samples.10.time", Val::F(0.75)),
("samples.10.totalTime", Val::F(3.75)), ("samples.10.progress", Val::F(0.75)), ("samples.10.totalProgress", Val::F(0.9375)),
("samples.10.x", Val::F(75.0)), ("samples.11.totalTime_set", Val::F(4.0)), ("samples.11.iteration", Val::F(3.0)), ("samples.11.time", Val::F(1.0)),
("samples.11.totalTime", Val::F(4.0)), ("samples.11.progress", Val::F(1.0)), ("samples.11.totalProgress", Val::F(1.0)), ("samples.11.x", Val::F(100.0)),
("samples.12.totalTime_set", Val::F(4.5)), ("samples.12.iteration", Val::F(3.0)), ("samples.12.time", Val::F(1.0)),
("samples.12.totalTime", Val::F(4.0)), ("samples.12.progress", Val::F(1.0)), ("samples.12.totalProgress", Val::F(1.0)), ("samples.12.x", Val::F(100.0)),
] },
PositionCase { id: "tweenTo_label", description: "tweenTo()/tweenFromTo() durations (tweens created paused)",
gsap_calls: &["tl.to(o,{x:100,duration:1,ease:\"none\"}); tl.to(o,{x:300,duration:2,ease:\"none\"}); tl.addLabel(\"mid\",1.5); tl.addLabel(\"end\",3)", "tw1 = tl.tweenTo(\"mid\",{paused:true}) // tl at time 0", "tl.time(1); tw2 = tl.tweenTo(\"end\",{paused:true})", "tl.time(0); tl.timeScale(2); tw3 = tl.tweenTo(\"end\",{paused:true}); tl.timeScale(1)", "tw4 = tl.tweenFromTo(\"mid\",\"end\",{paused:true})", "tl.time(0); tw5 = tl.tweenTo(\"end\",{paused:true}); tw5.totalTime(tw5.duration()/2); read tl.time()"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 2.0, total_duration: 2.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 1.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 1.0)],
labels: &[("mid", 1.5), ("end", 3.0)], labels_before_duration_call: &[("mid", 1.5), ("end", 3.0)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("tw1_duration", Val::F(1.5)), ("tw1_ease_is_none", Val::B(true)), ("tw2_duration", Val::F(2.0)), ("tw3_duration_timeScale2", Val::F(1.5)),
("tw4_fromTo_duration", Val::F(1.5)), ("tw5_half_tl_time", Val::F(1.5)), ("tw5_half_x", Val::F(150.0)), ("tw5_tl_paused_after", Val::B(true)),
("tw4_at0_tl_time", Val::F(1.5)), ("tw4_at_end_tl_time", Val::F(3.0)),
] },
PositionCase { id: "shiftChildren_1", description: "shiftChildren(1): labels NOT adjusted by default",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "tl.to(o,{duration:1},1) // B", "tl.addLabel(\"L\",1)", "tl.shiftChildren(1)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 1.0)],
start_times_before_duration_call: &[("A", 1.0), ("B", 2.0)],
labels: &[("L", 1.0)], labels_before_duration_call: &[("L", 1.0)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("before.children.len", Val::F(2.0)), ("before.children.0.len", Val::F(2.0)), ("before.children.0.0", Val::S("A")),
("before.children.0.1", Val::F(0.0)), ("before.children.1.len", Val::F(2.0)), ("before.children.1.0", Val::S("B")),
("before.children.1.1", Val::F(1.0)), ("before.labels.L", Val::F(1.0)), ("before.duration", Val::F(2.0)),
] },
PositionCase { id: "shiftChildren_1_adjustLabels", description: "shiftChildren(1, true): labels adjusted",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "tl.to(o,{duration:1},1) // B", "tl.addLabel(\"L\",1)", "tl.shiftChildren(1, true)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 1.0)],
start_times_before_duration_call: &[("A", 1.0), ("B", 2.0)],
labels: &[("L", 2.0)], labels_before_duration_call: &[("L", 2.0)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("before.children.len", Val::F(2.0)), ("before.children.0.len", Val::F(2.0)), ("before.children.0.0", Val::S("A")),
("before.children.0.1", Val::F(0.0)), ("before.children.1.len", Val::F(2.0)), ("before.children.1.0", Val::S("B")),
("before.children.1.1", Val::F(1.0)), ("before.labels.L", Val::F(1.0)), ("before.duration", Val::F(2.0)),
] },
PositionCase { id: "shiftChildren_1_ignoreBefore_1", description: "shiftChildren(1, true, 1): only children/labels with start >= 1 move",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "tl.to(o,{duration:1},1) // B", "tl.addLabel(\"L\",1)", "tl.shiftChildren(1, true, 1)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 1.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 2.0)],
labels: &[("L", 2.0)], labels_before_duration_call: &[("L", 2.0)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("before.children.len", Val::F(2.0)), ("before.children.0.len", Val::F(2.0)), ("before.children.0.0", Val::S("A")),
("before.children.0.1", Val::F(0.0)), ("before.children.1.len", Val::F(2.0)), ("before.children.1.0", Val::S("B")),
("before.children.1.1", Val::F(1.0)), ("before.labels.L", Val::F(1.0)), ("before.duration", Val::F(2.0)),
] },
PositionCase { id: "addPause_2", description: "addPause(2) inserts a zero-duration \"isPause\" child (and becomes recent); extra compares totalTime() vs render() across it on un-paused timelines",
gsap_calls: &["tl.to(o,{x:1,duration:1}) x3 (A,B,C at 0,1,2)", "tl.addPause(2)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1.0, duration: 1.0, total_duration: 1.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "<pause>", kind: "tween", data: Some("isPause"), start_time: 2.0, duration: 0.0, total_duration: 0.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("<pause>"),
start_time_at_add: &[("A", 0.0), ("B", 1.0), ("C", 2.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 1.0), ("C", 2.0), ("<pause>", 2.0)],
labels: &[], labels_before_duration_call: &[],
duration: 3.0, total_duration: 3.0, child_count: 4, timeline_start_time_in_parent: 0.0,
extra: &[
("unpaused_totalTime_1_5_then_2_5.time", Val::F(2.5)), ("unpaused_totalTime_1_5_then_2_5.paused", Val::B(false)),
("unpaused_totalTime_1_5_then_2_5.note", Val::S("totalTime()/seek() set _forcing=1 so pauses are NOT honored")),
("unpaused_render_1_5_then_2_5.time", Val::F(2.0)), ("unpaused_render_1_5_then_2_5.paused", Val::B(true)),
("unpaused_render_1_5_then_2_5.note", Val::S("render() (what the ticker drives through the parent, no _forcing): the playhead stops AT the pause and the timeline pauses itself")),
] },
PositionCase { id: "rounding_start_and_duration", description: "startTime and duration are rounded to 1e-7 (_roundPrecise)",
gsap_calls: &["tl.to(o,{duration:1/3},1/3) // A", "tl.to(o,{duration:0.123456789}) // B"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.3333333, duration: 0.3333333, total_duration: 0.3333333, end_time: 0.6666666, end_time_no_repeats: 0.6666666, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 0.6666666, duration: 0.1234568, total_duration: 0.1234568, end_time: 0.7901234, end_time_no_repeats: 0.7901234, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.3333333), ("B", 0.6666666)],
start_times_before_duration_call: &[("A", 0.3333333), ("B", 0.6666666)],
labels: &[], labels_before_duration_call: &[],
duration: 0.7901234, total_duration: 0.7901234, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("input_position_A", Val::F(0.3333333333333333)), ("input_duration_A", Val::F(0.3333333333333333)), ("input_duration_B", Val::F(0.123456789)),
] },
PositionCase { id: "design_t1_positions", description: "design 5.5 T1: every position form in one timeline",
gsap_calls: &["tl.to(o,{duration:1}) // A", "tl.to(o,{duration:2},\"-=0.5\") // B", "tl.to(o,{duration:1},\"<\") // C", "tl.to(o,{duration:1},\"+=1\") // D", "tl.addLabel(\"L\",\"<0.25\")", "tl.to(o,{duration:2},\">-0.5\") // E", "tl.to(o,{duration:1},\"L+=1\") // F", "tl.to(o,{duration:1},\"missing\") // G", "tl.to(o,{duration:2},\"<50%\") // H", "tl.to(o,{duration:2},\"<+=50%\") // I", "tl.to(o,{duration:1},\"+=50%\") // J"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 0.5, duration: 2.0, total_duration: 2.0, end_time: 2.5, end_time_no_repeats: 2.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 0.5, duration: 1.0, total_duration: 1.0, end_time: 1.5, end_time_no_repeats: 1.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 3.5, duration: 1.0, total_duration: 1.0, end_time: 4.5, end_time_no_repeats: 4.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "E", kind: "tween", data: None, start_time: 4.0, duration: 2.0, total_duration: 2.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "F", kind: "tween", data: None, start_time: 4.75, duration: 1.0, total_duration: 1.0, end_time: 5.75, end_time_no_repeats: 5.75, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "G", kind: "tween", data: None, start_time: 6.0, duration: 1.0, total_duration: 1.0, end_time: 7.0, end_time_no_repeats: 7.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "H", kind: "tween", data: None, start_time: 6.5, duration: 2.0, total_duration: 2.0, end_time: 8.5, end_time_no_repeats: 8.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "I", kind: "tween", data: None, start_time: 7.5, duration: 2.0, total_duration: 2.0, end_time: 9.5, end_time_no_repeats: 9.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "J", kind: "tween", data: None, start_time: 10.0, duration: 1.0, total_duration: 1.0, end_time: 11.0, end_time_no_repeats: 11.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"], recent: Some("J"),
start_time_at_add: &[("A", 0.0), ("B", 0.5), ("C", 0.5), ("D", 3.5), ("E", 4.0), ("F", 4.75), ("G", 6.0), ("H", 6.5), ("I", 7.5), ("J", 10.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 0.5), ("C", 0.5), ("D", 3.5), ("E", 4.0), ("F", 4.75), ("G", 6.0), ("H", 6.5), ("I", 7.5), ("J", 10.0)],
labels: &[("L", 3.75), ("missing", 6.0)], labels_before_duration_call: &[("L", 3.75), ("missing", 6.0)],
duration: 11.0, total_duration: 11.0, child_count: 10, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "design_t2_repeats_delays_infinite", description: "design 5.5 T2: \">\" after repeats, \"<\" includes the delay, repeat:-1 clips later default positions",
gsap_calls: &["tl.to(o,{duration:1,repeat:2,repeatDelay:0.5}) // A", "tl.to(o,{duration:1},\">\") // B", "tl.to(o,{duration:1,delay:0.5},\"<\") // C", "tl.to(o,{duration:1},\"<\") // D", "tl.to(o,{duration:1,repeat:-1}) // E", "tl.to(o,{duration:1},\">\") // F", "tl.to(o,{duration:1}) // G"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 4.0, end_time: 4.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 2.0 },
Child { name: "B", kind: "tween", data: None, start_time: 4.0, duration: 1.0, total_duration: 1.0, end_time: 5.0, end_time_no_repeats: 5.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 4.5, duration: 1.0, total_duration: 1.0, end_time: 5.5, end_time_no_repeats: 5.5, time_scale: 1.0, delay: 0.5, repeat: 0.0 },
Child { name: "D", kind: "tween", data: None, start_time: 4.5, duration: 1.0, total_duration: 1.0, end_time: 5.5, end_time_no_repeats: 5.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "E", kind: "tween", data: None, start_time: 5.5, duration: 1.0, total_duration: 10000000000.0, end_time: 10000000005.5, end_time_no_repeats: 6.5, time_scale: 1.0, delay: 0.0, repeat: -1.0 },
Child { name: "F", kind: "tween", data: None, start_time: 6.5, duration: 1.0, total_duration: 1.0, end_time: 7.5, end_time_no_repeats: 7.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "G", kind: "tween", data: None, start_time: 7.5, duration: 1.0, total_duration: 1.0, end_time: 8.5, end_time_no_repeats: 8.5, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C", "D", "E", "F", "G"], recent: Some("G"),
start_time_at_add: &[("A", 0.0), ("B", 4.0), ("C", 4.5), ("D", 4.5), ("E", 5.5), ("F", 6.5), ("G", 7.5)],
start_times_before_duration_call: &[("A", 0.0), ("B", 4.0), ("C", 4.5), ("D", 4.5), ("E", 5.5), ("F", 6.5), ("G", 7.5)],
labels: &[], labels_before_duration_call: &[],
duration: 10000000005.5, total_duration: 10000000005.5, child_count: 7, timeline_start_time_in_parent: 0.0,
extra: &[] },
PositionCase { id: "design_t3_negative_start_nested", description: "design 5.6.3 T3: timeline nested in a paused (non-smooth) parent: A at 0, label L at 0.5, B \"-=3\" -> -2; the shift moves children, not labels, not the inner start",
gsap_calls: &["outer = gsap.timeline({paused:true}); inner = gsap.timeline(); outer.add(inner, 0)", "inner.to(o,{duration:1}) // A", "inner.addLabel(\"L\", 0.5)", "inner.to(o,{duration:1},\"-=3\") // B", "inner.duration()"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", -2.0)],
start_times_before_duration_call: &[("B", -2.0), ("A", 0.0)],
labels: &[("L", 0.5)], labels_before_duration_call: &[("L", 0.5)],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[("inner_time_after", Val::F(0.0)), ("outer_duration", Val::F(3.0))] },
PositionCase { id: "design_t3_negative_start_root_level", description: "design 5.6.3 T3 on an UN-paused root-level timeline (root has smoothChildTiming): its own start moves back by 2 and its playhead reads 2",
gsap_calls: &["tl = gsap.timeline() // root never advances here", "tl.to(o,{duration:1}) // A", "tl.to(o,{duration:1},\"-=3\") // B", "tl.duration()"],
children: &[
Child { name: "B", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "A", kind: "tween", data: None, start_time: 2.0, duration: 1.0, total_duration: 1.0, end_time: 3.0, end_time_no_repeats: 3.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", -2.0)],
start_times_before_duration_call: &[("B", -2.0), ("A", 0.0)],
labels: &[], labels_before_duration_call: &[],
duration: 3.0, total_duration: 3.0, child_count: 2, timeline_start_time_in_parent: -2.0,
extra: &[("timeline_time_after", Val::F(2.0)), ("timeline_total_time_after", Val::F(2.0))] },
PositionCase { id: "design_round7_negative_ties", description: "round7 = Math.round(v*1e7)/1e7: JS rounds ties toward +Infinity, so -2.5e-7 -> -2e-7 and -1.5e-7 -> -1e-7 (Rust f64::round would give -3e-7 / -2e-7)",
gsap_calls: &["tl.to(o,{duration:1},-0.00000025) // A", "tl.to(o,{duration:1},-0.00000015) // B"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 1e-7, duration: 1.0, total_duration: 1.0, end_time: 1.0000001, end_time_no_repeats: 1.0000001, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", -2e-7), ("B", -1e-7)],
start_times_before_duration_call: &[("A", -2e-7), ("B", -1e-7)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0000001, total_duration: 1.0000001, child_count: 2, timeline_start_time_in_parent: f64::NEG_INFINITY,
extra: &[
("a_times_1e7", Val::F(-2.5)), ("b_times_1e7", Val::F(-1.5)), ("math_round_a", Val::F(-2.0)), ("math_round_b", Val::F(-1.0)),
] },
PositionCase { id: "design_empty_timeline_anchors", description: "anchors on an EMPTY timeline (no recent child): \">\" = 0, \">0.5\" = 0.5, \"<0.5\" = 0.5, \"+=1\" = 1, \"L\" creates L at 0",
gsap_calls: &["tl.to(o,{duration:1},\">\") // A (each extra value comes from its own fresh empty timeline)"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", 0.0)],
start_times_before_duration_call: &[("A", 0.0)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 1.0, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[
("gt", Val::F(0.0)), ("gt_0_5", Val::F(0.5)), ("lt_0_5", Val::F(0.5)), ("plus_1", Val::F(1.0)), ("label_l", Val::F(0.0)),
] },
PositionCase { id: "design_add_label_percent_quirk", description: "addLabel with a percent form and no child: \"+=50%\" is read as +50 SECONDS (GSAP quirk), \"<50%\" uses the recent child, \"<+=50%\" throws",
gsap_calls: &["tl.to(o,{duration:2}) // A", "tl.addLabel(\"p\",\"+=50%\")", "tl.addLabel(\"r\",\"<50%\")", "tl.addLabel(\"q\",\"<+=50%\") // throws TypeError"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 2.0, total_duration: 2.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A"], recent: Some("A"),
start_time_at_add: &[("A", 0.0)],
start_times_before_duration_call: &[("A", 0.0)],
labels: &[("p", 52.0), ("r", 1.0)], labels_before_duration_call: &[("p", 52.0), ("r", 1.0)],
duration: 2.0, total_duration: 2.0, child_count: 1, timeline_start_time_in_parent: 0.0,
extra: &[("lt_plus_50pct_throws", Val::B(true))] },
PositionCase { id: "design_zero_duration_repeat_tdur", description: "totalDuration of zero-duration tweens with repeat (the design ignores repeat on zero-duration nodes, D11)",
gsap_calls: &["gsap.to(o,{duration:0,repeat:2,paused:true}).totalDuration()", "gsap.to(o,{duration:0,repeat:-1,paused:true}).totalDuration()", "gsap.to(o,{duration:0,repeat:2,repeatDelay:0.5,paused:true}).totalDuration()"],
children: &[
],
insertion_order: &[], recent: None,
start_time_at_add: &[],
start_times_before_duration_call: &[],
labels: &[], labels_before_duration_call: &[],
duration: 0.0, total_duration: 0.0, child_count: 0, timeline_start_time_in_parent: 0.0,
extra: &[
("tdur_repeat_2", Val::F(0.0)), ("tdur_repeat_inf", Val::F(10000000000.0)), ("tdur_repeat_2_rdelay_0_5", Val::F(1.0)),
] },
PositionCase { id: "design_paused_child_duration", description: "a paused child does not count in the parent duration (child._ts test); resuming it does NOT uncache the parent (stale 1) until something else uncaches it",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "B = gsap.to(o,{duration:2,paused:true}); tl.add(B, 0)", "tl.duration()", "B.paused(false); tl.duration()", "tl.shiftChildren(0); tl.duration()"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 0.0, duration: 2.0, total_duration: 2.0, end_time: 2.0, end_time_no_repeats: 2.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B"], recent: Some("B"),
start_time_at_add: &[("A", 0.0), ("B", 0.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 0.0)],
labels: &[], labels_before_duration_call: &[],
duration: 1.0, total_duration: 1.0, child_count: 2, timeline_start_time_in_parent: 0.0,
extra: &[
("duration_with_b_paused", Val::F(1.0)), ("duration_after_resume", Val::F(1.0)), ("duration_after_uncache", Val::F(2.0)),
] },
PositionCase { id: "design_remove_recent", description: "tl.remove(recent) makes the LAST child (by start) the new recent",
gsap_calls: &["tl.to(o,{duration:1},0) // A", "tl.to(o,{duration:1},5) // B", "tl.to(o,{duration:1},2) // X", "tl.remove(X)", "tl.to(o,{duration:1},\"<\") // C"],
children: &[
Child { name: "A", kind: "tween", data: None, start_time: 0.0, duration: 1.0, total_duration: 1.0, end_time: 1.0, end_time_no_repeats: 1.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "B", kind: "tween", data: None, start_time: 5.0, duration: 1.0, total_duration: 1.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
Child { name: "C", kind: "tween", data: None, start_time: 5.0, duration: 1.0, total_duration: 1.0, end_time: 6.0, end_time_no_repeats: 6.0, time_scale: 1.0, delay: 0.0, repeat: 0.0 },
],
insertion_order: &["A", "B", "C"], recent: Some("C"),
start_time_at_add: &[("A", 0.0), ("B", 5.0), ("X", 2.0), ("C", 5.0)],
start_times_before_duration_call: &[("A", 0.0), ("B", 5.0), ("C", 5.0)],
labels: &[], labels_before_duration_call: &[],
duration: 6.0, total_duration: 6.0, child_count: 3, timeline_start_time_in_parent: 0.0,
extra: &[] },
];