(frames: MotionFrame[], a: string, b: string)
| 98 | } |
| 99 | |
| 100 | function before(frames: MotionFrame[], a: string, b: string): LayoutIssue[] { |
| 101 | const issues: LayoutIssue[] = []; |
| 102 | if (!everMatched(frames, a)) issues.push(missingIssue(a, 0)); |
| 103 | if (!everMatched(frames, b)) issues.push(missingIssue(b, 0)); |
| 104 | if (issues.length > 0) return issues; |
| 105 | |
| 106 | const appearA = firstAppear(frames, a); |
| 107 | const appearB = firstAppear(frames, b); |
| 108 | const timeA = appearA ? appearA.time : Number.POSITIVE_INFINITY; |
| 109 | const timeB = appearB ? appearB.time : Number.POSITIVE_INFINITY; |
| 110 | if (timeA < timeB) return []; |
| 111 | |
| 112 | const label = (t: number) => (Number.isFinite(t) ? `${round(t)}s` : "never"); |
| 113 | return [ |
| 114 | { |
| 115 | code: "motion_out_of_order", |
| 116 | severity: "error", |
| 117 | time: Number.isFinite(timeA) ? timeA : 0, |
| 118 | selector: a, |
| 119 | message: `${a} should appear before ${b}, but ${a} appears at ${label(timeA)} and ${b} at ${label(timeB)} — reorder the entrances`, |
| 120 | rect: appearA ? appearA.rect : ZERO_RECT, |
| 121 | fixHint: `Make ${a}'s entrance land before ${b}'s on the timeline.`, |
| 122 | }, |
| 123 | ]; |
| 124 | } |
| 125 | |
| 126 | function isOffFrame(r: LayoutRect, canvas: Canvas): boolean { |
| 127 | return ( |
no test coverage detected