@cardstack/choreo 0.0.0 → 0.1.0-unstable.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +55 -6
- package/addon-main.cjs +4 -0
- package/declarations/anchors.d.ts +26 -0
- package/declarations/anchors.d.ts.map +1 -0
- package/declarations/arming.d.ts +33 -0
- package/declarations/arming.d.ts.map +1 -0
- package/declarations/beacon.d.ts +9 -0
- package/declarations/beacon.d.ts.map +1 -0
- package/declarations/beacons.d.ts +23 -0
- package/declarations/beacons.d.ts.map +1 -0
- package/declarations/changeset.d.ts +46 -0
- package/declarations/changeset.d.ts.map +1 -0
- package/declarations/choreo.d.ts +261 -0
- package/declarations/choreo.d.ts.map +1 -0
- package/declarations/compile.d.ts +45 -0
- package/declarations/compile.d.ts.map +1 -0
- package/declarations/deliver.d.ts +44 -0
- package/declarations/deliver.d.ts.map +1 -0
- package/declarations/easings.d.ts +20 -0
- package/declarations/easings.d.ts.map +1 -0
- package/declarations/far.d.ts +33 -0
- package/declarations/far.d.ts.map +1 -0
- package/declarations/film/clip.d.ts +51 -0
- package/declarations/film/clip.d.ts.map +1 -0
- package/declarations/film/clips.d.ts +155 -0
- package/declarations/film/clips.d.ts.map +1 -0
- package/declarations/film/film.d.ts +1208 -0
- package/declarations/film/film.d.ts.map +1 -0
- package/declarations/film/graph/adjust.d.ts +112 -0
- package/declarations/film/graph/adjust.d.ts.map +1 -0
- package/declarations/film/graph/compile.d.ts +140 -0
- package/declarations/film/graph/compile.d.ts.map +1 -0
- package/declarations/film/graph/host.d.ts +67 -0
- package/declarations/film/graph/host.d.ts.map +1 -0
- package/declarations/film/graph/nodes.d.ts +287 -0
- package/declarations/film/graph/nodes.d.ts.map +1 -0
- package/declarations/film/index.d.ts +24 -0
- package/declarations/film/index.d.ts.map +1 -0
- package/declarations/film/joins.d.ts +143 -0
- package/declarations/film/joins.d.ts.map +1 -0
- package/declarations/film/math.d.ts +15 -0
- package/declarations/film/math.d.ts.map +1 -0
- package/declarations/film/overlays.d.ts +54 -0
- package/declarations/film/overlays.d.ts.map +1 -0
- package/declarations/film/picture.d.ts +104 -0
- package/declarations/film/picture.d.ts.map +1 -0
- package/declarations/film/plate.d.ts +37 -0
- package/declarations/film/plate.d.ts.map +1 -0
- package/declarations/film/player.d.ts +120 -0
- package/declarations/film/player.d.ts.map +1 -0
- package/declarations/film/rail.d.ts +40 -0
- package/declarations/film/rail.d.ts.map +1 -0
- package/declarations/film/schedule.d.ts +93 -0
- package/declarations/film/schedule.d.ts.map +1 -0
- package/declarations/film/seam.d.ts +38 -0
- package/declarations/film/seam.d.ts.map +1 -0
- package/declarations/film/titles.d.ts +37 -0
- package/declarations/film/titles.d.ts.map +1 -0
- package/declarations/film/types.d.ts +544 -0
- package/declarations/film/types.d.ts.map +1 -0
- package/declarations/film.d.ts +3 -0
- package/declarations/film.d.ts.map +1 -0
- package/declarations/gesture.d.ts +33 -0
- package/declarations/gesture.d.ts.map +1 -0
- package/declarations/index.d.ts +26 -0
- package/declarations/index.d.ts.map +1 -0
- package/declarations/measure.d.ts +29 -0
- package/declarations/measure.d.ts.map +1 -0
- package/declarations/path.d.ts +73 -0
- package/declarations/path.d.ts.map +1 -0
- package/declarations/registry.d.ts +46 -0
- package/declarations/registry.d.ts.map +1 -0
- package/declarations/run.d.ts +406 -0
- package/declarations/run.d.ts.map +1 -0
- package/declarations/space.d.ts +31 -0
- package/declarations/space.d.ts.map +1 -0
- package/declarations/steps.d.ts +479 -0
- package/declarations/steps.d.ts.map +1 -0
- package/declarations/test-support/index.d.ts +30 -0
- package/declarations/test-support/index.d.ts.map +1 -0
- package/declarations/types.d.ts +761 -0
- package/declarations/types.d.ts.map +1 -0
- package/dist/anchors.js +34 -0
- package/dist/anchors.js.map +1 -0
- package/dist/arming.js +120 -0
- package/dist/arming.js.map +1 -0
- package/dist/beacon.js +25 -0
- package/dist/beacon.js.map +1 -0
- package/dist/beacons.js +77 -0
- package/dist/beacons.js.map +1 -0
- package/dist/changeset.js +129 -0
- package/dist/changeset.js.map +1 -0
- package/dist/choreo.js +1026 -0
- package/dist/choreo.js.map +1 -0
- package/dist/compile.js +1403 -0
- package/dist/compile.js.map +1 -0
- package/dist/deliver.js +326 -0
- package/dist/deliver.js.map +1 -0
- package/dist/easings.js +39 -0
- package/dist/easings.js.map +1 -0
- package/dist/far.js +147 -0
- package/dist/far.js.map +1 -0
- package/dist/film/clip.js +100 -0
- package/dist/film/clip.js.map +1 -0
- package/dist/film/clips.js +147 -0
- package/dist/film/clips.js.map +1 -0
- package/dist/film/film.js +4424 -0
- package/dist/film/film.js.map +1 -0
- package/dist/film/graph/adjust.js +160 -0
- package/dist/film/graph/adjust.js.map +1 -0
- package/dist/film/graph/compile.js +225 -0
- package/dist/film/graph/compile.js.map +1 -0
- package/dist/film/graph/host.js +77 -0
- package/dist/film/graph/host.js.map +1 -0
- package/dist/film/graph/nodes.js +500 -0
- package/dist/film/graph/nodes.js.map +1 -0
- package/dist/film/index.js +18 -0
- package/dist/film/index.js.map +1 -0
- package/dist/film/joins.js +260 -0
- package/dist/film/joins.js.map +1 -0
- package/dist/film/math.js +49 -0
- package/dist/film/math.js.map +1 -0
- package/dist/film/overlays.js +66 -0
- package/dist/film/overlays.js.map +1 -0
- package/dist/film/picture.js +58 -0
- package/dist/film/picture.js.map +1 -0
- package/dist/film/plate.js +36 -0
- package/dist/film/plate.js.map +1 -0
- package/dist/film/player.js +79 -0
- package/dist/film/player.js.map +1 -0
- package/dist/film/rail.js +38 -0
- package/dist/film/rail.js.map +1 -0
- package/dist/film/schedule.js +238 -0
- package/dist/film/schedule.js.map +1 -0
- package/dist/film/seam.js +64 -0
- package/dist/film/seam.js.map +1 -0
- package/dist/film/titles.js +45 -0
- package/dist/film/titles.js.map +1 -0
- package/dist/film/types.js +2 -0
- package/dist/film/types.js.map +1 -0
- package/dist/film.js +18 -0
- package/dist/film.js.map +1 -0
- package/dist/gesture.js +111 -0
- package/dist/gesture.js.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/measure.js +99 -0
- package/dist/measure.js.map +1 -0
- package/dist/path.js +272 -0
- package/dist/path.js.map +1 -0
- package/dist/registry.js +55 -0
- package/dist/registry.js.map +1 -0
- package/dist/run.js +2423 -0
- package/dist/run.js.map +1 -0
- package/dist/space.js +57 -0
- package/dist/space.js.map +1 -0
- package/dist/steps.js +831 -0
- package/dist/steps.js.map +1 -0
- package/dist/test-support/index.js +150 -0
- package/dist/test-support/index.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +201 -6
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { fn } from '@ember/helper';
|
|
2
|
+
import { on } from '@ember/modifier';
|
|
3
|
+
import Component from '@glimmer/component';
|
|
4
|
+
import { motion } from 'glimmer-motion';
|
|
5
|
+
import { Choreo } from '../choreo.js';
|
|
6
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
7
|
+
import { setComponentTemplate } from '@ember/component';
|
|
8
|
+
|
|
9
|
+
/** what the hand is pointing at on the bar: the chapter and the time */
|
|
10
|
+
/**
|
|
11
|
+
* THE PLAYER. The grammar every viewer knows from a decade of video on the
|
|
12
|
+
* web: one full-width bar with the chapters as segments, a knob that sits
|
|
13
|
+
* exactly where the fill ends, the transport on the left, the modes on
|
|
14
|
+
* the right, the time beside the chapter's name, and the whole apparatus
|
|
15
|
+
* fading off the picture when nobody is touching it. The bar listens: a
|
|
16
|
+
* resting pointer gets the chapter and the time in a bubble and a ghost
|
|
17
|
+
* fill to where it would cut, and every button names itself and its key.
|
|
18
|
+
* The playhead is honest: a film cut on a chased lens cannot seek, so the
|
|
19
|
+
* drag reads as time and the release RE-CUTS from the shot under the hand.
|
|
20
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
21
|
+
class Player extends Component {
|
|
22
|
+
static {
|
|
23
|
+
setComponentTemplate(precompileTemplate("<div class=\"cf-player {{if @idle \"is-idle\"}} {{if @away \"is-away\"}}\">\n <div class=\"cf-scrub {{if @scrubAt \"is-live\"}}\" role=\"slider\" aria-label=\"playhead\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"0\" tabindex=\"0\" {{@slider}} {{on \"pointerdown\" @scrubDown}} {{on \"pointermove\" @scrubMove}} {{on \"pointerup\" @scrubUp}} {{on \"pointerleave\" @scrubLeave}}>\n <span class=\"cf-scrub-track\" {{@trackWrap}}>\n {{#each @playbar as |c|}}\n <span class=\"cf-scrub-ch\" style={{c.style}}>\n <span class=\"cf-scrub-ghost\"></span>\n <span class=\"cf-scrub-fill\"></span>\n </span>\n {{/each}}\n <span class=\"cf-scrub-head\"></span>\n {{#if @tip}}\n <span class=\"cf-scrub-tip\">\n <span class=\"cf-scrub-tip-ch\">{{@tip.ch}}</span>\n <span class=\"cf-scrub-tip-t\">{{@tip.t}}</span>\n </span>\n {{/if}}\n </span>\n </div>\n\n <div class=\"cf-controls\">\n <button type=\"button\" class=\"cf-ic\" aria-label={{if @live \"pause\" \"play\"}} data-tip={{if @live \"Pause (k)\" \"Play (k)\"}} {{on \"click\" @toggle}}>\n {{#if @live}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M7 5h4v14H7zM13 5h4v14h-4z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" /></svg>\n {{/if}}\n </button>\n <button type=\"button\" class=\"cf-ic\" aria-label=\"previous chapter\" data-tip=\"Previous chapter (\u2190)\" {{on \"click\" @prev}}>\n <svg viewBox=\"0 0 24 24\"><path d=\"M6 6h2v12H6zM18 6l-9 6 9 6z\" /></svg>\n </button>\n <button type=\"button\" class=\"cf-ic\" aria-label=\"next chapter\" data-tip=\"Next chapter (\u2192)\" {{on \"click\" @next}}>\n <svg viewBox=\"0 0 24 24\"><path d=\"M16 6h2v12h-2zM6 6l9 6-9 6z\" /></svg>\n </button>\n {{!-- the speaker and, on a hover, the fader beside it --}}\n <span class=\"cf-vol\">\n <button type=\"button\" class=\"cf-ic\" aria-label={{if @sound \"mute\" \"sound\"}} data-tip={{if @sound \"Mute (m)\" \"Unmute (m)\"}} {{on \"click\" @hear}}>\n {{#if (eq @loud \"high\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2zm-2-7.6v2.1a5.6 5.6 0 0 1 0 11v2.1a7.7 7.7 0 0 0 0-15.2z\" /></svg>\n {{else if (eq @loud \"low\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4z\" /></svg>\n {{/if}}\n </button>\n <input class=\"cf-vol-range\" type=\"range\" min=\"0\" max=\"1\" step=\"0.02\" value={{@vol}} aria-label=\"volume\" style={{@volStyle}} {{on \"input\" @fade}} />\n </span>\n <span class=\"cf-time\">\n <b>{{@clock}}</b>\n <i>/</i>\n {{@duration}}\n </span>\n {{!-- the chapter's name, beside the clock, is the way into the list --}}\n <button type=\"button\" class=\"cf-chap\" data-tip=\"Chapters (c)\" {{on \"click\" @toc}}>\n <span>\xB7</span>\n {{@chapter.n}}\n {{@chapter.title}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8.6 6 7.2 7.4 11.8 12l-4.6 4.6L8.6 18l6-6z\" /></svg>\n </button>\n <span class=\"cf-spacer\"></span>\n {{#if @debug}}\n <span class=\"cf-build\">{{@build}}</span>\n {{/if}}\n <button type=\"button\" class=\"cf-ic cf-ic-cc {{if @subsOn \"is-on\"}}\" aria-label=\"captions\" data-tip=\"Captions (v)\" {{on \"click\" @cc}}>CC</button>\n {{!-- the way back to the door, with its name on it: a title screen\n is a place, not a loop --}}\n <button type=\"button\" class=\"cf-word\" data-tip=\"Back to the title screen (0)\" {{on \"click\" @restart}}>\n {{!-- a title card, not a loop: the glyph is the card itself --}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M3 5h18v14H3zm2 2v10h14V7zm3 3h8v1.6H8zm0 3.4h5V15H8z\" /></svg>\n Title screen\n </button>\n <button type=\"button\" class=\"cf-ic cf-ic-last\" aria-label={{if @full \"exit fullscreen\" \"fullscreen\"}} data-tip={{if @full \"Exit full screen (f)\" \"Full screen (f)\"}} {{on \"click\" @screen}}>\n {{#if @full}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M9 4H7v3H4v2h5zM4 15h3v3h2v-5H4zm11 3h2v-3h3v-2h-5zm0-14v5h5V7h-3V4z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 4h5v2H6v3H4zm11 0h5v5h-2V6h-3zM4 15h2v3h3v2H4zm14 0h2v5h-5v-2h3z\" /></svg>\n {{/if}}\n </button>\n </div>\n</div>", {
|
|
24
|
+
strictMode: true,
|
|
25
|
+
scope: () => ({
|
|
26
|
+
on,
|
|
27
|
+
eq
|
|
28
|
+
})
|
|
29
|
+
}), this);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* THE CONFIRMATION GLYPH: a play or a pause, once, in the middle of the
|
|
34
|
+
* picture, gone in half a second — the way every player answers a tap
|
|
35
|
+
* on the glass.
|
|
36
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
37
|
+
class Burst extends Component {
|
|
38
|
+
static {
|
|
39
|
+
setComponentTemplate(precompileTemplate("<div class=\"cf-burst\" {{on \"animationend\" @done}}>\n {{#if (eq @kind \"play\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M7 5h4v14H7zM13 5h4v14h-4z\" /></svg>\n {{/if}}\n</div>", {
|
|
40
|
+
strictMode: true,
|
|
41
|
+
scope: () => ({
|
|
42
|
+
on,
|
|
43
|
+
eq
|
|
44
|
+
})
|
|
45
|
+
}), this);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** a chapter, as the menu lists it */
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* THE DISC MENU. A film with chapters owes the viewer a way into them,
|
|
52
|
+
* and the arrow keys alone are a secret. Picking one re-cuts the score
|
|
53
|
+
* from that chapter's head — the same move the arrows make, because a
|
|
54
|
+
* skip here is an edit and never a seek. The film keeps running behind
|
|
55
|
+
* it, blurred: a menu that freezes the picture makes it feel like a file.
|
|
56
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
57
|
+
class Menu extends Component {
|
|
58
|
+
static {
|
|
59
|
+
setComponentTemplate(precompileTemplate("<Choreo class=\"cf-menu\" as |m|>\n <div class=\"cf-menu-in\" {{motion id=\"menu\" role=\"sheet\"}}>\n <p class=\"cf-menu-head\">{{@title}}</p>\n <p class=\"cf-menu-sub\">{{@sub}}</p>\n <ol class=\"cf-menu-list\">\n {{#each @contents as |c|}}\n <li>\n <button type=\"button\" class=\"cf-menu-item {{if c.here \"is-here\"}}\" {{on \"click\" (fn @pick c.head)}}>\n <span class=\"cf-menu-n\">{{c.n}}</span>\n <span class=\"cf-menu-t\">{{c.title}}</span>\n <span class=\"cf-menu-d\">{{c.shots}} shots</span>\n </button>\n </li>\n {{/each}}\n </ol>\n <p class=\"cf-menu-keys\">{{if @keys @keys \"space / K play \xB7 \u2190 \u2192 chapter \xB7 1\u20139 chapter \xB7 M sound \xB7 F full screen \xB7 V captions \xB7 0 title screen \xB7 C close\"}}</p>\n </div>\n <m.Tween @of={{m.inserted \"sheet\"}} @y={{array 26 0}} @scale={{array 0.97 1}} @opacity={{array 0 1}} @duration={{0.42}} @ease={{array 0.22 1 0.36 1}} />\n <m.Tween @of={{m.removed \"sheet\"}} @y={{array 0 18}} @opacity={{array 1 0}} @duration={{0.2}} @ease=\"easeIn\" />\n</Choreo>", {
|
|
60
|
+
strictMode: true,
|
|
61
|
+
scope: () => ({
|
|
62
|
+
Choreo,
|
|
63
|
+
motion,
|
|
64
|
+
on,
|
|
65
|
+
fn,
|
|
66
|
+
array
|
|
67
|
+
})
|
|
68
|
+
}), this);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function array(...items) {
|
|
72
|
+
return items;
|
|
73
|
+
}
|
|
74
|
+
function eq(a, b) {
|
|
75
|
+
return a === b;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { Burst, Menu, Player };
|
|
79
|
+
//# sourceMappingURL=player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.js","sources":["../../src/film/player.gts"],"sourcesContent":["import { fn } from '@ember/helper';\nimport { on } from '@ember/modifier';\nimport Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { Chapter, ElementModifier } from './types.ts';\n\n/** one chapter as a segment of the scrub bar */\nexport interface PlaybarSegment {\n head: number;\n n: string;\n style: string;\n title: string;\n}\n\n/** what the hand is pointing at on the bar: the chapter and the time */\nexport interface ScrubTip {\n ch: string;\n t: string;\n}\n\nexport interface PlayerSignature {\n Args: {\n /** the front door is up: the transport waits outside */\n away: boolean;\n /** which cut of the film this is; shown only under debug */\n build?: string;\n /** toggle captions */\n cc: () => void;\n chapter: Chapter;\n clock: string;\n debug?: boolean;\n duration: string;\n /** the fader moved */\n fade: (e: Event) => void;\n full: boolean;\n hear: () => void;\n /** nobody has touched the frame for a while: the apparatus leaves */\n idle: boolean;\n /** rolling AND not held by the viewer: what the play button shows */\n live: boolean;\n /** sound on, and the fader up: the speaker glyph's three states */\n loud: 'high' | 'low' | 'off';\n menu: boolean;\n next: () => void;\n playbar: PlaybarSegment[];\n prev: () => void;\n restart: () => void;\n screen: () => void;\n /** the fraction the hand is holding while scrubbing, else null */\n scrubAt: null | number;\n scrubDown: (event: Event) => void;\n scrubLeave: () => void;\n scrubMove: (event: Event) => void;\n scrubUp: (event: Event) => void;\n /** keeps the bar's element, for the slider's live value */\n slider: ElementModifier;\n sound: boolean;\n subsOn: boolean;\n /** the bubble over the bar, while a pointer is on it */\n tip: null | ScrubTip;\n toc: () => void;\n toggle: () => void;\n /** measures the track, so the head can sit at the fill's end in pixels */\n trackWrap: ElementModifier;\n /** the master fader, 0..1 */\n vol: number;\n /** the fader's value as a custom property, for its own fill */\n volStyle: string;\n };\n}\n\n/**\n * THE PLAYER. The grammar every viewer knows from a decade of video on the\n * web: one full-width bar with the chapters as segments, a knob that sits\n * exactly where the fill ends, the transport on the left, the modes on\n * the right, the time beside the chapter's name, and the whole apparatus\n * fading off the picture when nobody is touching it. The bar listens: a\n * resting pointer gets the chapter and the time in a bubble and a ghost\n * fill to where it would cut, and every button names itself and its key.\n * The playhead is honest: a film cut on a chased lens cannot seek, so the\n * drag reads as time and the release RE-CUTS from the shot under the hand.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Player extends Component<PlayerSignature> {\n <template>\n <div class='cf-player {{if @idle \"is-idle\"}} {{if @away \"is-away\"}}'>\n <div\n class='cf-scrub {{if @scrubAt \"is-live\"}}'\n role='slider'\n aria-label='playhead'\n aria-valuemin='0'\n aria-valuemax='100'\n aria-valuenow='0'\n tabindex='0'\n {{@slider}}\n {{on 'pointerdown' @scrubDown}}\n {{on 'pointermove' @scrubMove}}\n {{on 'pointerup' @scrubUp}}\n {{on 'pointerleave' @scrubLeave}}\n >\n <span class='cf-scrub-track' {{@trackWrap}}>\n {{#each @playbar as |c|}}\n <span class='cf-scrub-ch' style={{c.style}}>\n <span class='cf-scrub-ghost'></span>\n <span class='cf-scrub-fill'></span>\n </span>\n {{/each}}\n <span class='cf-scrub-head'></span>\n {{#if @tip}}\n <span class='cf-scrub-tip'>\n <span class='cf-scrub-tip-ch'>{{@tip.ch}}</span>\n <span class='cf-scrub-tip-t'>{{@tip.t}}</span>\n </span>\n {{/if}}\n </span>\n </div>\n\n <div class='cf-controls'>\n <button\n type='button'\n class='cf-ic'\n aria-label={{if @live 'pause' 'play'}}\n data-tip={{if @live 'Pause (k)' 'Play (k)'}}\n {{on 'click' @toggle}}\n >\n {{#if @live}}\n <svg viewBox='0 0 24 24'><path\n d='M7 5h4v14H7zM13 5h4v14h-4z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>\n {{/if}}\n </button>\n <button\n type='button'\n class='cf-ic'\n aria-label='previous chapter'\n data-tip='Previous chapter (←)'\n {{on 'click' @prev}}\n >\n <svg viewBox='0 0 24 24'><path\n d='M6 6h2v12H6zM18 6l-9 6 9 6z'\n /></svg>\n </button>\n <button\n type='button'\n class='cf-ic'\n aria-label='next chapter'\n data-tip='Next chapter (→)'\n {{on 'click' @next}}\n >\n <svg viewBox='0 0 24 24'><path\n d='M16 6h2v12h-2zM6 6l9 6-9 6z'\n /></svg>\n </button>\n {{! the speaker and, on a hover, the fader beside it }}\n <span class='cf-vol'>\n <button\n type='button'\n class='cf-ic'\n aria-label={{if @sound 'mute' 'sound'}}\n data-tip={{if @sound 'Mute (m)' 'Unmute (m)'}}\n {{on 'click' @hear}}\n >\n {{#if (eq @loud 'high')}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2zm-2-7.6v2.1a5.6 5.6 0 0 1 0 11v2.1a7.7 7.7 0 0 0 0-15.2z'\n /></svg>\n {{else if (eq @loud 'low')}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm12.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4z'\n /></svg>\n {{/if}}\n </button>\n <input\n class='cf-vol-range'\n type='range'\n min='0'\n max='1'\n step='0.02'\n value={{@vol}}\n aria-label='volume'\n style={{@volStyle}}\n {{on 'input' @fade}}\n />\n </span>\n <span class='cf-time'>\n <b>{{@clock}}</b>\n <i>/</i>\n {{@duration}}\n </span>\n {{! the chapter's name, beside the clock, is the way into the list }}\n <button\n type='button'\n class='cf-chap'\n data-tip='Chapters (c)'\n {{on 'click' @toc}}\n >\n <span>·</span>\n {{@chapter.n}}\n {{@chapter.title}}\n <svg viewBox='0 0 24 24'><path\n d='M8.6 6 7.2 7.4 11.8 12l-4.6 4.6L8.6 18l6-6z'\n /></svg>\n </button>\n <span class='cf-spacer'></span>\n {{#if @debug}}\n <span class='cf-build'>{{@build}}</span>\n {{/if}}\n <button\n type='button'\n class='cf-ic cf-ic-cc {{if @subsOn \"is-on\"}}'\n aria-label='captions'\n data-tip='Captions (v)'\n {{on 'click' @cc}}\n >CC</button>\n {{! the way back to the door, with its name on it: a title screen\n is a place, not a loop }}\n <button\n type='button'\n class='cf-word'\n data-tip='Back to the title screen (0)'\n {{on 'click' @restart}}\n >\n {{! a title card, not a loop: the glyph is the card itself }}\n <svg viewBox='0 0 24 24'><path\n d='M3 5h18v14H3zm2 2v10h14V7zm3 3h8v1.6H8zm0 3.4h5V15H8z'\n /></svg>\n Title screen\n </button>\n <button\n type='button'\n class='cf-ic cf-ic-last'\n aria-label={{if @full 'exit fullscreen' 'fullscreen'}}\n data-tip={{if @full 'Exit full screen (f)' 'Full screen (f)'}}\n {{on 'click' @screen}}\n >\n {{#if @full}}\n <svg viewBox='0 0 24 24'><path\n d='M9 4H7v3H4v2h5zM4 15h3v3h2v-5H4zm11 3h2v-3h3v-2h-5zm0-14v5h5V7h-3V4z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path\n d='M4 4h5v2H6v3H4zm11 0h5v5h-2V6h-3zM4 15h2v3h3v2H4zm14 0h2v5h-5v-2h3z'\n /></svg>\n {{/if}}\n </button>\n </div>\n </div>\n </template>\n}\n\nexport interface BurstSignature {\n Args: {\n /** the animation ran out: take the glyph down */\n done: () => void;\n kind: 'pause' | 'play';\n };\n}\n\n/**\n * THE CONFIRMATION GLYPH: a play or a pause, once, in the middle of the\n * picture, gone in half a second — the way every player answers a tap\n * on the glass.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Burst extends Component<BurstSignature> {\n <template>\n <div class='cf-burst' {{on 'animationend' @done}}>\n {{#if (eq @kind 'play')}}\n <svg viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path d='M7 5h4v14H7zM13 5h4v14h-4z' /></svg>\n {{/if}}\n </div>\n </template>\n}\n\n/** a chapter, as the menu lists it */\nexport interface MenuEntry {\n head: number;\n here: boolean;\n n: string;\n secs: number;\n shots: number;\n title: string;\n}\n\nexport interface MenuSignature {\n Args: {\n contents: MenuEntry[];\n /** the key legend under the list */\n keys?: string;\n pick: (head: number) => void;\n sub: string;\n title: string;\n };\n}\n\n/**\n * THE DISC MENU. A film with chapters owes the viewer a way into them,\n * and the arrow keys alone are a secret. Picking one re-cuts the score\n * from that chapter's head — the same move the arrows make, because a\n * skip here is an edit and never a seek. The film keeps running behind\n * it, blurred: a menu that freezes the picture makes it feel like a file.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Menu extends Component<MenuSignature> {\n <template>\n <Choreo class='cf-menu' as |m|>\n <div class='cf-menu-in' {{motion id='menu' role='sheet'}}>\n <p class='cf-menu-head'>{{@title}}</p>\n <p class='cf-menu-sub'>{{@sub}}</p>\n <ol class='cf-menu-list'>\n {{#each @contents as |c|}}\n <li>\n <button\n type='button'\n class='cf-menu-item {{if c.here \"is-here\"}}'\n {{on 'click' (fn @pick c.head)}}\n >\n <span class='cf-menu-n'>{{c.n}}</span>\n <span class='cf-menu-t'>{{c.title}}</span>\n <span class='cf-menu-d'>{{c.shots}} shots</span>\n </button>\n </li>\n {{/each}}\n </ol>\n <p class='cf-menu-keys'>{{if\n @keys\n @keys\n 'space / K play · ← → chapter · 1–9 chapter · M sound · F full screen · V captions · 0 title screen · C close'\n }}</p>\n </div>\n <m.Tween\n @of={{m.inserted 'sheet'}}\n @y={{array 26 0}}\n @scale={{array 0.97 1}}\n @opacity={{array 0 1}}\n @duration={{0.42}}\n @ease={{array 0.22 1 0.36 1}}\n />\n <m.Tween\n @of={{m.removed 'sheet'}}\n @y={{array 0 18}}\n @opacity={{array 1 0}}\n @duration={{0.2}}\n @ease='easeIn'\n />\n </Choreo>\n </template>\n}\n\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n\nfunction eq(a: unknown, b: unknown): boolean {\n return a === b;\n}\n"],"names":["Player","Component","setComponentTemplate","precompileTemplate","strictMode","scope","on","eq","Burst","Menu","Choreo","motion","fn","array","items","a","b"],"mappings":";;;;;;;;AAgBA;AAyDA;;;;;;;;;;AAUC;AAEM,MAAMA,eAAeC,SAAA,CAAU;AACpC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,2hJAAA,EAyKA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAUA;;;;AAIC;AAEM,MAAMC,cAAcP,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,yQAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;;AAqBA;;;;;;AAMC;AAEM,MAAME,aAAaR,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,8mCAAA,EA0CA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAK,MAAA;QAAAC,MAAA;QAAAL,EAAA;QAAAM,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASA,KAAAA,CAAS,GAAGC,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;AAEA,SAASP,GAAGQ,CAAU,EAAEC,CAAU,EAAU;EAC1C,OAAOD,CAAA,KAAMC,CAAA;AACf;;;;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { fn } from '@ember/helper';
|
|
2
|
+
import { on } from '@ember/modifier';
|
|
3
|
+
import Component from '@glimmer/component';
|
|
4
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
5
|
+
import { setComponentTemplate } from '@ember/component';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* THE FILM ON ONE LINE. A rule the width of the chapter strip, the run
|
|
9
|
+
* so far filled in the accent, a dot on the head and the readout riding
|
|
10
|
+
* it; the chapters are dots at the places they open, so the distance
|
|
11
|
+
* between them is a fact and not a layout. The dots are the doors into
|
|
12
|
+
* the chapters. The head's position is a custom property (`--cf-head`)
|
|
13
|
+
* written by the film every frame, so nothing here re-renders on the
|
|
14
|
+
* clock.
|
|
15
|
+
*/
|
|
16
|
+
class Rail extends Component {
|
|
17
|
+
atStyle = m => `left:calc(5px + ${m.at.toFixed(4)} * (100% - 10px))`;
|
|
18
|
+
static {
|
|
19
|
+
setComponentTemplate(precompileTemplate("<div class=\"cf-rail\">\n <span class=\"cf-rail-n\">{{@chapter.n}}</span>\n <span class=\"cf-rail-t\">{{@chapter.title}}</span>\n <span class=\"cf-years\">\n <i class=\"cf-years-rule\" aria-hidden=\"true\"></i>\n <i class=\"cf-years-fill\" aria-hidden=\"true\"></i>\n {{#each @marks as |m|}}\n <button type=\"button\" class=\"cf-years-ch {{if m.here \"is-here\"}} {{if m.done \"is-done\"}}\" style={{this.atStyle m}} title=\"{{m.n}} {{m.title}}{{if m.label (concat \" \xB7 \" m.label)}}\" {{on \"click\" (fn @pick m.head)}}></button>\n {{/each}}\n {{#if @readout}}\n <b class=\"cf-years-now\" aria-hidden=\"true\">{{@readout}}</b>\n {{else}}\n <b class=\"cf-years-now is-mute\" aria-hidden=\"true\"></b>\n {{/if}}\n {{#if @labels}}\n <em class=\"cf-years-a\" aria-hidden=\"true\">{{get @labels 0}}</em>\n <em class=\"cf-years-b\" aria-hidden=\"true\">{{get @labels 1}}</em>\n {{/if}}\n </span>\n</div>", {
|
|
20
|
+
strictMode: true,
|
|
21
|
+
scope: () => ({
|
|
22
|
+
concat,
|
|
23
|
+
on,
|
|
24
|
+
fn,
|
|
25
|
+
get
|
|
26
|
+
})
|
|
27
|
+
}), this);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function concat(...parts) {
|
|
31
|
+
return parts.map(p => String(p ?? '')).join('');
|
|
32
|
+
}
|
|
33
|
+
function get(list, i) {
|
|
34
|
+
return list[i];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Rail };
|
|
38
|
+
//# sourceMappingURL=rail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rail.js","sources":["../../src/film/rail.gts"],"sourcesContent":["import { fn } from '@ember/helper';\nimport { on } from '@ember/modifier';\nimport Component from '@glimmer/component';\n\nimport type { Chapter } from './types.ts';\n\n/** a chapter as a door on the rule */\nexport interface RailMark {\n /** where on the rule, 0..1 */\n at: number;\n done: boolean;\n head: number;\n here: boolean;\n /** the label the door announces when the rule is a clock */\n label?: string;\n n: string;\n title: string;\n}\n\nexport interface RailSignature {\n Args: {\n /** the chapter on screen */\n chapter: Chapter;\n /** the ends of the rule, as the film writes them; empty when the rule is time */\n labels?: [string, string];\n marks: RailMark[];\n /** cut to a chapter's head */\n pick: (head: number) => void;\n /** the readout riding the head — the year, or nothing */\n readout?: string;\n };\n}\n\n/**\n * THE FILM ON ONE LINE. A rule the width of the chapter strip, the run\n * so far filled in the accent, a dot on the head and the readout riding\n * it; the chapters are dots at the places they open, so the distance\n * between them is a fact and not a layout. The dots are the doors into\n * the chapters. The head's position is a custom property (`--cf-head`)\n * written by the film every frame, so nothing here re-renders on the\n * clock.\n */\nexport class Rail extends Component<RailSignature> {\n atStyle = (m: RailMark): string =>\n `left:calc(5px + ${m.at.toFixed(4)} * (100% - 10px))`;\n\n <template>\n <div class='cf-rail'>\n <span class='cf-rail-n'>{{@chapter.n}}</span>\n <span class='cf-rail-t'>{{@chapter.title}}</span>\n <span class='cf-years'>\n <i class='cf-years-rule' aria-hidden='true'></i>\n <i class='cf-years-fill' aria-hidden='true'></i>\n {{#each @marks as |m|}}\n <button\n type='button'\n class='cf-years-ch {{if m.here \"is-here\"}} {{if m.done \"is-done\"}}'\n style={{this.atStyle m}}\n title='{{m.n}} {{m.title}}{{if m.label (concat \" · \" m.label)}}'\n {{on 'click' (fn @pick m.head)}}\n ></button>\n {{/each}}\n {{#if @readout}}\n <b class='cf-years-now' aria-hidden='true'>{{@readout}}</b>\n {{else}}\n <b class='cf-years-now is-mute' aria-hidden='true'></b>\n {{/if}}\n {{#if @labels}}\n <em class='cf-years-a' aria-hidden='true'>{{get @labels 0}}</em>\n <em class='cf-years-b' aria-hidden='true'>{{get @labels 1}}</em>\n {{/if}}\n </span>\n </div>\n </template>\n}\n\nfunction concat(...parts: unknown[]): string {\n return parts.map((p) => String(p ?? '')).join('');\n}\n\nfunction get<T>(list: readonly T[], i: number): T | undefined {\n return list[i];\n}\n"],"names":["Rail","Component","atStyle","m","at","toFixed","setComponentTemplate","precompileTemplate","strictMode","scope","concat","on","fn","get","parts","map","p","String","join","list","i"],"mappings":";;;;;;AAiCA;;;;;;;;;AASO,MAAMA,IAAA,SAAaC,SAAA,CAAU;AAClCC,EAAAA,OAAA,GAAWC,CAAG,IACZ,CAAA,gBAAA,EAAmBA,CAAA,CAAEC,EAAE,CAACC,OAAO,CAAC,CAAA,CAAA,CAAA,iBAAA,CAAqB;AAEvD,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,+7BAAA,EA2BA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,EAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASH,OAAO,GAAGI,KAAgB,EAAS;AAC1C,EAAA,OAAOA,KAAA,CAAMC,GAAG,CAAEC,KAAMC,MAAA,CAAOD,CAAA,IAAK,EAAA,CAAA,CAAA,CAAKE,IAAI,CAAC,EAAA,CAAA;AAChD;AAEA,SAASL,GAAAA,CAAOM,IAAkB,EAAEC,CAAS,EAAgB;EAC3D,OAAOD,IAAI,CAACC,CAAA,CAAE;AAChB;;;;"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { lerp } from './math.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* THE SCHEDULE — everything a film derives from its shot list before a
|
|
5
|
+
* single frame is drawn, as pure functions of the table.
|
|
6
|
+
*
|
|
7
|
+
* A film is its cue table and its camera path: when a beat starts, what
|
|
8
|
+
* fires when, where the lens is at every waypoint, how the chapters are
|
|
9
|
+
* cut. These were getters on the engine; lifting them here changes no
|
|
10
|
+
* number — the engine calls them — and lets the same arithmetic be run
|
|
11
|
+
* headless, in Node, to write the golden fixtures the graph refactor is
|
|
12
|
+
* measured against (`scripts/film-fixtures.mjs`,
|
|
13
|
+
* `tests/unit/film-schedule-test.ts`).
|
|
14
|
+
*
|
|
15
|
+
* Nothing in here reads the clock, the page or the DOM. Everything is a
|
|
16
|
+
* function of `Beat[]` and a few numbers, which is the property the
|
|
17
|
+
* fixtures pin.
|
|
18
|
+
*/
|
|
19
|
+
/** one tick of the shot list, in seconds: a beat's `ticks` × this is its length */
|
|
20
|
+
const TICK = 2;
|
|
21
|
+
|
|
22
|
+
/** the whole film, in seconds, leads included */
|
|
23
|
+
function totalSecs(beats) {
|
|
24
|
+
return beats.reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : b.lead ?? 0), 0) * TICK;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** film seconds before a beat's nominal head, leads included */
|
|
28
|
+
function secsBefore(beats, index) {
|
|
29
|
+
return beats.slice(0, index).reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : b.lead ?? 0), 0) * TICK;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* FILM SECONDS AT WHICH A BEAT'S OWN SHOT BEGINS. The cues fire one tick
|
|
34
|
+
* after the beat table says, because the pose-in-force seed occupies the
|
|
35
|
+
* spline's first slot — so the windows the exact clock derives carry the
|
|
36
|
+
* same offset, and the picture and the type agree with the cut film to
|
|
37
|
+
* the frame. This is the single most load-bearing constant in the film.
|
|
38
|
+
*/
|
|
39
|
+
function beatStart(beats, i) {
|
|
40
|
+
if (i <= 0) {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
return secsBefore(beats, i) + (beats[i].lead ?? 0) * TICK + TICK;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** one cue per beat (and an `air` cue ahead of a led beat), as delays into the score */
|
|
47
|
+
|
|
48
|
+
function cues(beats) {
|
|
49
|
+
let t = 0;
|
|
50
|
+
const out = [];
|
|
51
|
+
beats.forEach((b, i) => {
|
|
52
|
+
const lead = i === 0 ? 0 : b.lead ?? 0;
|
|
53
|
+
if (lead > 0) {
|
|
54
|
+
out.push({
|
|
55
|
+
action: 'air',
|
|
56
|
+
delay: t + TICK,
|
|
57
|
+
index: String(i)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const at = t + lead * TICK;
|
|
61
|
+
t = at + b.ticks * TICK;
|
|
62
|
+
/* cue 0 is exempt from the offset: the boot applies the head beat by
|
|
63
|
+
hand and a guard swallows its immediate re-fire */
|
|
64
|
+
out.push({
|
|
65
|
+
action: 'beat',
|
|
66
|
+
delay: at === 0 ? 0 : at + TICK,
|
|
67
|
+
index: String(i)
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** the first beat of each chapter, in whole-film indices */
|
|
74
|
+
function chapterHeads(beats) {
|
|
75
|
+
const heads = [];
|
|
76
|
+
beats.forEach((b, i) => {
|
|
77
|
+
if (heads.length === 0 || beats[heads[heads.length - 1]].ch !== b.ch) {
|
|
78
|
+
heads.push(i);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return heads;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** the chapter menu's rows: head index, numeral, title, shot count, running time */
|
|
85
|
+
|
|
86
|
+
function contents(beats, chapters) {
|
|
87
|
+
const heads = chapterHeads(beats);
|
|
88
|
+
return heads.map((head, i) => {
|
|
89
|
+
const nextHead = heads[i + 1] ?? beats.length;
|
|
90
|
+
const ch = chapters[beats[head].ch] ?? chapters[0];
|
|
91
|
+
return {
|
|
92
|
+
head,
|
|
93
|
+
n: ch.n,
|
|
94
|
+
secs: beats.slice(head, nextHead).reduce((t, b) => t + b.ticks, 0) * TICK,
|
|
95
|
+
shots: nextHead - head,
|
|
96
|
+
title: ch.title
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** the seam a beat is entered through, or none when it neither cuts nor names one */
|
|
102
|
+
function joinInto(beats, i, defaultJoin) {
|
|
103
|
+
const beat = beats[i];
|
|
104
|
+
return beat.cut || beat.join ? beat.join ?? defaultJoin : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* WHERE A SHOT ENDS — the beat's authored tail, floored so it always reads
|
|
109
|
+
* as a move, and clamped so it never travels past the pose the next shot
|
|
110
|
+
* begins on. Shared by the path (which lerps its waypoints across it) and
|
|
111
|
+
* by the launch: a cut hands the chaser this shot's own speed.
|
|
112
|
+
*
|
|
113
|
+
* A shot must move on screen, not on paper: frame-differencing a capture
|
|
114
|
+
* of the cut showed authored tails of a few degrees reading as stills, so
|
|
115
|
+
* a tail is a DIRECTION and a floor, not a distance — what the beat asks
|
|
116
|
+
* for is honoured, and anything slower than a real slow move is stretched
|
|
117
|
+
* up to one. The floors are per second. And it never travels past where
|
|
118
|
+
* the next shot begins: at a cut that jumped BACKWARDS into the successor.
|
|
119
|
+
*/
|
|
120
|
+
function tailFor(beats, bi) {
|
|
121
|
+
const b = beats[bi];
|
|
122
|
+
const drift = b.toCam ?? {
|
|
123
|
+
...b.cam,
|
|
124
|
+
dolly: b.cam.dolly * 1.05,
|
|
125
|
+
pitch: b.cam.pitch + 1.4,
|
|
126
|
+
yaw: b.cam.yaw + 6
|
|
127
|
+
};
|
|
128
|
+
const next = beats[bi + 1];
|
|
129
|
+
const secs = b.ticks * TICK;
|
|
130
|
+
const sgn = d => d < 0 ? -1 : 1;
|
|
131
|
+
const stretch = (key, floor) => {
|
|
132
|
+
const head = b.cam[key] ?? 0;
|
|
133
|
+
const d = (drift[key] ?? 0) - head;
|
|
134
|
+
const nose = next ? (next.cam[key] ?? 0) - head : 0;
|
|
135
|
+
/* the direction is the beat's own, or the next shot's if the beat
|
|
136
|
+
asked for nothing at all */
|
|
137
|
+
const way = d !== 0 ? sgn(d) : nose !== 0 ? sgn(nose) : 1;
|
|
138
|
+
const reach = !next ? Infinity : sgn(nose) === way && nose !== 0 ? Math.abs(nose) : 0;
|
|
139
|
+
const want = Math.min(Math.max(Math.abs(d), floor), Math.max(Math.abs(d), reach));
|
|
140
|
+
return head + way * want;
|
|
141
|
+
};
|
|
142
|
+
const to = {
|
|
143
|
+
...drift,
|
|
144
|
+
dolly: stretch('dolly', b.cam.dolly * Math.min(0.22, 0.022 * secs)),
|
|
145
|
+
ox: stretch('ox', Math.min(0.07, 0.008 * secs)),
|
|
146
|
+
pitch: stretch('pitch', Math.min(6, 0.45 * secs)),
|
|
147
|
+
yaw: stretch('yaw', Math.min(22, 2 * secs))
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
dolly: to.dolly,
|
|
151
|
+
fx: to.fx ?? b.cam.fx ?? 0,
|
|
152
|
+
fz: to.fz ?? b.cam.fz ?? 0,
|
|
153
|
+
lookY: to.lookY,
|
|
154
|
+
ox: to.ox,
|
|
155
|
+
pitch: to.pitch,
|
|
156
|
+
yaw: to.yaw
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** one waypoint of the camera spline, in the terms `c.Camera3D @through` takes */
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* THE PATH. Every beat contributes `ticks` waypoints, lerped from its
|
|
164
|
+
* head to its tail, so waypoint count is screen time; a led beat first
|
|
165
|
+
* contributes `lead` waypoints travelling in from the previous tail; a
|
|
166
|
+
* `cut` beat splices the spline. `recut` marks the first beat as a splice
|
|
167
|
+
* too — a cut film re-cut from a chapter's head has nothing behind it.
|
|
168
|
+
*/
|
|
169
|
+
function waypoints(beats, recut = false) {
|
|
170
|
+
const pts = [];
|
|
171
|
+
for (const [bi, b] of beats.entries()) {
|
|
172
|
+
const splice = b.cut === true || bi === 0 && recut;
|
|
173
|
+
const to = tailFor(beats, bi);
|
|
174
|
+
const lead = bi === 0 ? 0 : b.lead ?? 0;
|
|
175
|
+
const prev = pts[pts.length - 1];
|
|
176
|
+
if (lead > 0 && prev) {
|
|
177
|
+
for (let k = 1; k <= lead; k++) {
|
|
178
|
+
const f = k / (lead + 1);
|
|
179
|
+
pts.push({
|
|
180
|
+
dolly: lerp(prev.dolly, b.cam.dolly, f),
|
|
181
|
+
look: {
|
|
182
|
+
x: lerp(prev.look.x, b.cam.fx ?? 0, f),
|
|
183
|
+
y: lerp(prev.look.y, b.cam.lookY, f),
|
|
184
|
+
z: lerp(prev.look.z, b.cam.fz ?? 0, f)
|
|
185
|
+
},
|
|
186
|
+
pitch: lerp(prev.pitch, b.cam.pitch, f),
|
|
187
|
+
x: lerp(prev.x, b.cam.ox ?? 0, f),
|
|
188
|
+
y: 0,
|
|
189
|
+
yaw: lerp(prev.yaw, b.cam.yaw, f)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (let k = 0; k < b.ticks; k++) {
|
|
194
|
+
const f = b.ticks === 1 ? 0 : k / (b.ticks - 1);
|
|
195
|
+
pts.push({
|
|
196
|
+
cut: splice && k === 0 ? true : undefined,
|
|
197
|
+
dolly: lerp(b.cam.dolly, to.dolly, f),
|
|
198
|
+
look: {
|
|
199
|
+
x: lerp(b.cam.fx ?? 0, to.fx ?? 0, f),
|
|
200
|
+
y: lerp(b.cam.lookY, to.lookY, f),
|
|
201
|
+
z: lerp(b.cam.fz ?? 0, to.fz ?? 0, f)
|
|
202
|
+
},
|
|
203
|
+
pitch: lerp(b.cam.pitch, to.pitch, f),
|
|
204
|
+
x: lerp(b.cam.ox ?? 0, to.ox ?? 0, f),
|
|
205
|
+
y: 0,
|
|
206
|
+
yaw: lerp(b.cam.yaw, to.yaw, f)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return pts;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* THE GOLDEN SCHEDULE: one object per film, everything above at once, in
|
|
215
|
+
* the shape the fixtures are written in. A refactor of the engine that
|
|
216
|
+
* changes any number in here has changed the film.
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
function schedule(beats, chapters, defaultJoin) {
|
|
220
|
+
return {
|
|
221
|
+
beats: beats.map((b, i) => ({
|
|
222
|
+
ch: b.ch,
|
|
223
|
+
id: b.id,
|
|
224
|
+
join: joinInto(beats, i, defaultJoin),
|
|
225
|
+
lead: i === 0 ? 0 : b.lead ?? 0,
|
|
226
|
+
secsBefore: secsBefore(beats, i),
|
|
227
|
+
start: beatStart(beats, i),
|
|
228
|
+
ticks: b.ticks
|
|
229
|
+
})),
|
|
230
|
+
contents: contents(beats, chapters),
|
|
231
|
+
cues: cues(beats),
|
|
232
|
+
total: totalSecs(beats),
|
|
233
|
+
waypoints: waypoints(beats)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { TICK, beatStart, chapterHeads, contents, cues, joinInto, schedule, secsBefore, tailFor, totalSecs, waypoints };
|
|
238
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","sources":["../../src/film/schedule.ts"],"sourcesContent":["/**\n * THE SCHEDULE — everything a film derives from its shot list before a\n * single frame is drawn, as pure functions of the table.\n *\n * A film is its cue table and its camera path: when a beat starts, what\n * fires when, where the lens is at every waypoint, how the chapters are\n * cut. These were getters on the engine; lifting them here changes no\n * number — the engine calls them — and lets the same arithmetic be run\n * headless, in Node, to write the golden fixtures the graph refactor is\n * measured against (`scripts/film-fixtures.mjs`,\n * `tests/unit/film-schedule-test.ts`).\n *\n * Nothing in here reads the clock, the page or the DOM. Everything is a\n * function of `Beat[]` and a few numbers, which is the property the\n * fixtures pin.\n */\nimport { lerp } from './math.ts';\nimport type { Beat, Cam, Chapter, Join, JoinName } from './types.ts';\n\n/** one tick of the shot list, in seconds: a beat's `ticks` × this is its length */\nexport const TICK = 2;\n\n/** the whole film, in seconds, leads included */\nexport function totalSecs(beats: Beat[]): number {\n return (\n beats.reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : (b.lead ?? 0)), 0) *\n TICK\n );\n}\n\n/** film seconds before a beat's nominal head, leads included */\nexport function secsBefore(beats: Beat[], index: number): number {\n return (\n beats\n .slice(0, index)\n .reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : (b.lead ?? 0)), 0) *\n TICK\n );\n}\n\n/**\n * FILM SECONDS AT WHICH A BEAT'S OWN SHOT BEGINS. The cues fire one tick\n * after the beat table says, because the pose-in-force seed occupies the\n * spline's first slot — so the windows the exact clock derives carry the\n * same offset, and the picture and the type agree with the cut film to\n * the frame. This is the single most load-bearing constant in the film.\n */\nexport function beatStart(beats: Beat[], i: number): number {\n if (i <= 0) {\n return 0;\n }\n return secsBefore(beats, i) + (beats[i]!.lead ?? 0) * TICK + TICK;\n}\n\n/** one cue per beat (and an `air` cue ahead of a led beat), as delays into the score */\nexport interface Cue {\n action: 'air' | 'beat';\n delay: number;\n index: string;\n}\n\nexport function cues(beats: Beat[]): Cue[] {\n let t = 0;\n const out: Cue[] = [];\n beats.forEach((b, i) => {\n const lead = i === 0 ? 0 : (b.lead ?? 0);\n if (lead > 0) {\n out.push({ action: 'air', delay: t + TICK, index: String(i) });\n }\n const at = t + lead * TICK;\n t = at + b.ticks * TICK;\n /* cue 0 is exempt from the offset: the boot applies the head beat by\n hand and a guard swallows its immediate re-fire */\n out.push({\n action: 'beat',\n delay: at === 0 ? 0 : at + TICK,\n index: String(i),\n });\n });\n return out;\n}\n\n/** the first beat of each chapter, in whole-film indices */\nexport function chapterHeads(beats: Beat[]): number[] {\n const heads: number[] = [];\n beats.forEach((b, i) => {\n if (heads.length === 0 || beats[heads[heads.length - 1]!]!.ch !== b.ch) {\n heads.push(i);\n }\n });\n return heads;\n}\n\n/** the chapter menu's rows: head index, numeral, title, shot count, running time */\nexport interface Content {\n head: number;\n n: string;\n secs: number;\n shots: number;\n title: string;\n}\n\nexport function contents(beats: Beat[], chapters: Chapter[]): Content[] {\n const heads = chapterHeads(beats);\n return heads.map((head, i) => {\n const nextHead = heads[i + 1] ?? beats.length;\n const ch = chapters[beats[head]!.ch] ?? chapters[0]!;\n return {\n head,\n n: ch.n,\n secs: beats.slice(head, nextHead).reduce((t, b) => t + b.ticks, 0) * TICK,\n shots: nextHead - head,\n title: ch.title,\n };\n });\n}\n\n/** the seam a beat is entered through, or none when it neither cuts nor names one */\nexport function joinInto(\n beats: Beat[],\n i: number,\n defaultJoin: Join,\n): JoinName | null {\n const beat = beats[i]!;\n return beat.cut || beat.join ? (beat.join ?? defaultJoin) : null;\n}\n\n/**\n * WHERE A SHOT ENDS — the beat's authored tail, floored so it always reads\n * as a move, and clamped so it never travels past the pose the next shot\n * begins on. Shared by the path (which lerps its waypoints across it) and\n * by the launch: a cut hands the chaser this shot's own speed.\n *\n * A shot must move on screen, not on paper: frame-differencing a capture\n * of the cut showed authored tails of a few degrees reading as stills, so\n * a tail is a DIRECTION and a floor, not a distance — what the beat asks\n * for is honoured, and anything slower than a real slow move is stretched\n * up to one. The floors are per second. And it never travels past where\n * the next shot begins: at a cut that jumped BACKWARDS into the successor.\n */\nexport function tailFor(beats: Beat[], bi: number): Cam {\n const b = beats[bi]!;\n const drift = b.toCam ?? {\n ...b.cam,\n dolly: b.cam.dolly * 1.05,\n pitch: b.cam.pitch + 1.4,\n yaw: b.cam.yaw + 6,\n };\n const next = beats[bi + 1];\n const secs = b.ticks * TICK;\n const sgn = (d: number) => (d < 0 ? -1 : 1);\n const stretch = (\n key: 'dolly' | 'ox' | 'pitch' | 'yaw',\n floor: number,\n ): number => {\n const head = b.cam[key] ?? 0;\n const d = (drift[key] ?? 0) - head;\n const nose = next ? (next.cam[key] ?? 0) - head : 0;\n /* the direction is the beat's own, or the next shot's if the beat\n asked for nothing at all */\n const way = d !== 0 ? sgn(d) : nose !== 0 ? sgn(nose) : 1;\n const reach = !next\n ? Infinity\n : sgn(nose) === way && nose !== 0\n ? Math.abs(nose)\n : 0;\n const want = Math.min(\n Math.max(Math.abs(d), floor),\n Math.max(Math.abs(d), reach),\n );\n return head + way * want;\n };\n const to = {\n ...drift,\n dolly: stretch('dolly', b.cam.dolly * Math.min(0.22, 0.022 * secs)),\n ox: stretch('ox', Math.min(0.07, 0.008 * secs)),\n pitch: stretch('pitch', Math.min(6, 0.45 * secs)),\n yaw: stretch('yaw', Math.min(22, 2 * secs)),\n };\n return {\n dolly: to.dolly,\n fx: to.fx ?? b.cam.fx ?? 0,\n fz: to.fz ?? b.cam.fz ?? 0,\n lookY: to.lookY,\n ox: to.ox,\n pitch: to.pitch,\n yaw: to.yaw,\n };\n}\n\n/** one waypoint of the camera spline, in the terms `c.Camera3D @through` takes */\nexport interface Waypoint {\n cut?: boolean;\n dolly: number;\n look: { x: number; y: number; z: number };\n pitch: number;\n x: number;\n y: number;\n yaw: number;\n}\n\n/**\n * THE PATH. Every beat contributes `ticks` waypoints, lerped from its\n * head to its tail, so waypoint count is screen time; a led beat first\n * contributes `lead` waypoints travelling in from the previous tail; a\n * `cut` beat splices the spline. `recut` marks the first beat as a splice\n * too — a cut film re-cut from a chapter's head has nothing behind it.\n */\nexport function waypoints(beats: Beat[], recut = false): Waypoint[] {\n const pts: Waypoint[] = [];\n for (const [bi, b] of beats.entries()) {\n const splice = b.cut === true || (bi === 0 && recut);\n const to = tailFor(beats, bi);\n const lead = bi === 0 ? 0 : (b.lead ?? 0);\n const prev = pts[pts.length - 1];\n if (lead > 0 && prev) {\n for (let k = 1; k <= lead; k++) {\n const f = k / (lead + 1);\n pts.push({\n dolly: lerp(prev.dolly, b.cam.dolly, f),\n look: {\n x: lerp(prev.look.x, b.cam.fx ?? 0, f),\n y: lerp(prev.look.y, b.cam.lookY, f),\n z: lerp(prev.look.z, b.cam.fz ?? 0, f),\n },\n pitch: lerp(prev.pitch, b.cam.pitch, f),\n x: lerp(prev.x, b.cam.ox ?? 0, f),\n y: 0,\n yaw: lerp(prev.yaw, b.cam.yaw, f),\n });\n }\n }\n for (let k = 0; k < b.ticks; k++) {\n const f = b.ticks === 1 ? 0 : k / (b.ticks - 1);\n pts.push({\n cut: splice && k === 0 ? true : undefined,\n dolly: lerp(b.cam.dolly, to.dolly, f),\n look: {\n x: lerp(b.cam.fx ?? 0, to.fx ?? 0, f),\n y: lerp(b.cam.lookY, to.lookY, f),\n z: lerp(b.cam.fz ?? 0, to.fz ?? 0, f),\n },\n pitch: lerp(b.cam.pitch, to.pitch, f),\n x: lerp(b.cam.ox ?? 0, to.ox ?? 0, f),\n y: 0,\n yaw: lerp(b.cam.yaw, to.yaw, f),\n });\n }\n }\n return pts;\n}\n\n/**\n * THE GOLDEN SCHEDULE: one object per film, everything above at once, in\n * the shape the fixtures are written in. A refactor of the engine that\n * changes any number in here has changed the film.\n */\nexport interface Schedule {\n beats: {\n ch: number;\n id: string;\n join: JoinName | null;\n lead: number;\n secsBefore: number;\n start: number;\n ticks: number;\n }[];\n contents: Content[];\n cues: Cue[];\n total: number;\n waypoints: Waypoint[];\n}\n\nexport function schedule(\n beats: Beat[],\n chapters: Chapter[],\n defaultJoin: Join,\n): Schedule {\n return {\n beats: beats.map((b, i) => ({\n ch: b.ch,\n id: b.id,\n join: joinInto(beats, i, defaultJoin),\n lead: i === 0 ? 0 : (b.lead ?? 0),\n secsBefore: secsBefore(beats, i),\n start: beatStart(beats, i),\n ticks: b.ticks,\n })),\n contents: contents(beats, chapters),\n cues: cues(beats),\n total: totalSecs(beats),\n waypoints: waypoints(beats),\n };\n}\n"],"names":["TICK","totalSecs","beats","reduce","n","b","i","ticks","lead","secsBefore","index","slice","beatStart","cues","t","out","forEach","push","action","delay","String","at","chapterHeads","heads","length","ch","contents","chapters","map","head","nextHead","secs","shots","title","joinInto","defaultJoin","beat","cut","join","tailFor","bi","drift","toCam","cam","dolly","pitch","yaw","next","sgn","d","stretch","key","floor","nose","way","reach","Infinity","Math","abs","want","min","max","to","ox","fx","fz","lookY","waypoints","recut","pts","entries","splice","prev","k","f","lerp","look","x","y","z","undefined","schedule","id","start","total"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE SEAM'S GATE: hold the cut until its still can be PAINTED.
|
|
3
|
+
*
|
|
4
|
+
* Every join that carries the outgoing frame carries it as a JPEG data
|
|
5
|
+
* URL in the DOM, and a full-resolution JPEG handed to the DOM is not
|
|
6
|
+
* paintable on the frame it is handed over — the browser decodes it off
|
|
7
|
+
* the main thread. The lens, meanwhile, moves on that frame. So the seam
|
|
8
|
+
* used to play backwards: three frames of the incoming shot at 60 Hz,
|
|
9
|
+
* then the still of the outgoing one, then the transition over it.
|
|
10
|
+
*
|
|
11
|
+
* The gate decodes the frame into the memory cache first and holds the
|
|
12
|
+
* whole cut — the lens snap, the overlay, an iris's projection — until
|
|
13
|
+
* it is ready; the DOM image then paints from that decode on the frame
|
|
14
|
+
* it is inserted. The outgoing shot stays live and moving for those few
|
|
15
|
+
* milliseconds, which nobody can see: a cut two frames late reads as a
|
|
16
|
+
* cut, a cut that shows the wrong picture does not.
|
|
17
|
+
*
|
|
18
|
+
* Three rules the film depends on:
|
|
19
|
+
*
|
|
20
|
+
* - **Nothing to decode, nothing to wait for.** A seam with no still (a
|
|
21
|
+
* cut, a flash, a dissolve the page held in its own glass) runs
|
|
22
|
+
* SYNCHRONOUSLY, on the caller's stack, exactly as it always did.
|
|
23
|
+
* - **One seam at a time.** A cut that arrives while an earlier one is
|
|
24
|
+
* still waiting retires it; the earlier one never runs.
|
|
25
|
+
* - **No seam waits for ever.** A decode that never comes back (a
|
|
26
|
+
* truncated capture, a browser without `HTMLImageElement.decode`) is
|
|
27
|
+
* given `cap` milliseconds and then the cut goes anyway.
|
|
28
|
+
*/
|
|
29
|
+
class Seam {
|
|
30
|
+
/** which cut is in hand; a later one retires an earlier one */
|
|
31
|
+
turn = 0;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Hold `cut` until `still` can be painted, or run it now if there is
|
|
35
|
+
* no still. `cap` is the longest the film will wait on a decode.
|
|
36
|
+
*/
|
|
37
|
+
hold(still, cut, cap = 120) {
|
|
38
|
+
if (!still) {
|
|
39
|
+
cut();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const mine = this.turn += 1;
|
|
43
|
+
let ran = false;
|
|
44
|
+
const go = () => {
|
|
45
|
+
if (ran || mine !== this.turn) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
ran = true;
|
|
49
|
+
cut();
|
|
50
|
+
};
|
|
51
|
+
const pre = new Image();
|
|
52
|
+
pre.src = still;
|
|
53
|
+
setTimeout(go, cap);
|
|
54
|
+
const decoding = pre.decode?.();
|
|
55
|
+
if (decoding) {
|
|
56
|
+
void decoding.then(go, go);
|
|
57
|
+
} else {
|
|
58
|
+
go();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { Seam };
|
|
64
|
+
//# sourceMappingURL=seam.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seam.js","sources":["../../src/film/seam.ts"],"sourcesContent":["/**\n * THE SEAM'S GATE: hold the cut until its still can be PAINTED.\n *\n * Every join that carries the outgoing frame carries it as a JPEG data\n * URL in the DOM, and a full-resolution JPEG handed to the DOM is not\n * paintable on the frame it is handed over — the browser decodes it off\n * the main thread. The lens, meanwhile, moves on that frame. So the seam\n * used to play backwards: three frames of the incoming shot at 60 Hz,\n * then the still of the outgoing one, then the transition over it.\n *\n * The gate decodes the frame into the memory cache first and holds the\n * whole cut — the lens snap, the overlay, an iris's projection — until\n * it is ready; the DOM image then paints from that decode on the frame\n * it is inserted. The outgoing shot stays live and moving for those few\n * milliseconds, which nobody can see: a cut two frames late reads as a\n * cut, a cut that shows the wrong picture does not.\n *\n * Three rules the film depends on:\n *\n * - **Nothing to decode, nothing to wait for.** A seam with no still (a\n * cut, a flash, a dissolve the page held in its own glass) runs\n * SYNCHRONOUSLY, on the caller's stack, exactly as it always did.\n * - **One seam at a time.** A cut that arrives while an earlier one is\n * still waiting retires it; the earlier one never runs.\n * - **No seam waits for ever.** A decode that never comes back (a\n * truncated capture, a browser without `HTMLImageElement.decode`) is\n * given `cap` milliseconds and then the cut goes anyway.\n */\nexport class Seam {\n /** which cut is in hand; a later one retires an earlier one */\n private turn = 0;\n\n /**\n * Hold `cut` until `still` can be painted, or run it now if there is\n * no still. `cap` is the longest the film will wait on a decode.\n */\n hold(still: string, cut: () => void, cap = 120): void {\n if (!still) {\n cut();\n return;\n }\n const mine = (this.turn += 1);\n let ran = false;\n const go = () => {\n if (ran || mine !== this.turn) {\n return;\n }\n ran = true;\n cut();\n };\n const pre = new Image();\n pre.src = still;\n setTimeout(go, cap);\n const decoding = pre.decode?.();\n if (decoding) {\n void decoding.then(go, go);\n } else {\n go();\n }\n }\n}\n"],"names":["Seam","turn","hold","still","cut","cap","mine","ran","go","pre","Image","src","setTimeout","decoding","decode","then"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,IAAI,CAAC;AAChB;AACQC,EAAAA,IAAI,GAAG,CAAC;;AAEhB;AACF;AACA;AACA;EACEC,IAAIA,CAACC,KAAa,EAAEC,GAAe,EAAEC,GAAG,GAAG,GAAG,EAAQ;IACpD,IAAI,CAACF,KAAK,EAAE;AACVC,MAAAA,GAAG,EAAE;AACL,MAAA;AACF,IAAA;AACA,IAAA,MAAME,IAAI,GAAI,IAAI,CAACL,IAAI,IAAI,CAAE;IAC7B,IAAIM,GAAG,GAAG,KAAK;IACf,MAAMC,EAAE,GAAGA,MAAM;AACf,MAAA,IAAID,GAAG,IAAID,IAAI,KAAK,IAAI,CAACL,IAAI,EAAE;AAC7B,QAAA;AACF,MAAA;AACAM,MAAAA,GAAG,GAAG,IAAI;AACVH,MAAAA,GAAG,EAAE;IACP,CAAC;AACD,IAAA,MAAMK,GAAG,GAAG,IAAIC,KAAK,EAAE;IACvBD,GAAG,CAACE,GAAG,GAAGR,KAAK;AACfS,IAAAA,UAAU,CAACJ,EAAE,EAAEH,GAAG,CAAC;AACnB,IAAA,MAAMQ,QAAQ,GAAGJ,GAAG,CAACK,MAAM,IAAI;AAC/B,IAAA,IAAID,QAAQ,EAAE;AACZ,MAAA,KAAKA,QAAQ,CAACE,IAAI,CAACP,EAAE,EAAEA,EAAE,CAAC;AAC5B,IAAA,CAAC,MAAM;AACLA,MAAAA,EAAE,EAAE;AACN,IAAA;AACF,EAAA;AACF;;;;"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { motion } from 'glimmer-motion';
|
|
3
|
+
import { Choreo } from '../choreo.js';
|
|
4
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
5
|
+
import { setComponentTemplate } from '@ember/component';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* THE GATE — the front door. The film is narrated, so the door asks; and
|
|
9
|
+
* the click that answers is the same gesture autoplay policy wants. The
|
|
10
|
+
* scene stands behind it, already seated on the opening frame and
|
|
11
|
+
* turning. What the door SAYS is the film's: the block is the front
|
|
12
|
+
* matter, set in the title package (`cf-matter`, `cf-mg-*`), and the
|
|
13
|
+
* shell here is the museum frame and the wash the type stands on.
|
|
14
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
15
|
+
class Gate extends Component {
|
|
16
|
+
static {
|
|
17
|
+
setComponentTemplate(precompileTemplate("<div class=\"cf-gate\">\n {{!-- the museum frame: a hairline border drawn just inside the screen,\n the way a plate is matted --}}\n <i class=\"cf-gate-frame\" aria-hidden=\"true\"></i>\n {{yield @film}}\n</div>", {
|
|
18
|
+
strictMode: true
|
|
19
|
+
}), this);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* THE END CARD. It arrives a moment after the last line has settled, over
|
|
24
|
+
* the held final frame — the film does not loop past its own ending;
|
|
25
|
+
* watching again is the viewer's choice. Back matter is the same package
|
|
26
|
+
* as the front, run in reverse order of importance.
|
|
27
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
28
|
+
class EndCard extends Component {
|
|
29
|
+
static {
|
|
30
|
+
setComponentTemplate(precompileTemplate("<Choreo class=\"cf-end\" as |m|>\n <div class=\"cf-end-in cf-matter\" {{motion id=\"end\" role=\"card\"}}>\n {{yield @film}}\n </div>\n <m.Tween @of={{m.inserted \"card\"}} @delay={{0.7}} @opacity={{array 0 1}} @y={{array 18 0}} @duration={{0.9}} @ease={{array 0.22 1 0.36 1}} />\n <m.Tween @of={{m.removed \"card\"}} @opacity={{array 1 0}} @duration={{0.2}} @ease=\"easeIn\" />\n</Choreo>", {
|
|
31
|
+
strictMode: true,
|
|
32
|
+
scope: () => ({
|
|
33
|
+
Choreo,
|
|
34
|
+
motion,
|
|
35
|
+
array
|
|
36
|
+
})
|
|
37
|
+
}), this);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function array(...items) {
|
|
41
|
+
return items;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { EndCard, Gate };
|
|
45
|
+
//# sourceMappingURL=titles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"titles.js","sources":["../../src/film/titles.gts"],"sourcesContent":["import Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { FilmHandle } from './types.ts';\n\nexport interface GateSignature {\n Args: {\n film: FilmHandle;\n };\n Blocks: {\n default: [FilmHandle];\n };\n}\n\n/**\n * THE GATE — the front door. The film is narrated, so the door asks; and\n * the click that answers is the same gesture autoplay policy wants. The\n * scene stands behind it, already seated on the opening frame and\n * turning. What the door SAYS is the film's: the block is the front\n * matter, set in the title package (`cf-matter`, `cf-mg-*`), and the\n * shell here is the museum frame and the wash the type stands on.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Gate extends Component<GateSignature> {\n <template>\n <div class='cf-gate'>\n {{! the museum frame: a hairline border drawn just inside the screen,\n the way a plate is matted }}\n <i class='cf-gate-frame' aria-hidden='true'></i>\n {{yield @film}}\n </div>\n </template>\n}\n\nexport interface EndCardSignature {\n Args: {\n film: FilmHandle;\n };\n Blocks: {\n default: [FilmHandle];\n };\n}\n\n/**\n * THE END CARD. It arrives a moment after the last line has settled, over\n * the held final frame — the film does not loop past its own ending;\n * watching again is the viewer's choice. Back matter is the same package\n * as the front, run in reverse order of importance.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class EndCard extends Component<EndCardSignature> {\n <template>\n <Choreo class='cf-end' as |m|>\n <div class='cf-end-in cf-matter' {{motion id='end' role='card'}}>\n {{yield @film}}\n </div>\n <m.Tween\n @of={{m.inserted 'card'}}\n @delay={{0.7}}\n @opacity={{array 0 1}}\n @y={{array 18 0}}\n @duration={{0.9}}\n @ease={{array 0.22 1 0.36 1}}\n />\n <m.Tween\n @of={{m.removed 'card'}}\n @opacity={{array 1 0}}\n @duration={{0.2}}\n @ease='easeIn'\n />\n </Choreo>\n </template>\n}\n\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n"],"names":["Gate","Component","setComponentTemplate","precompileTemplate","strictMode","EndCard","scope","Choreo","motion","array","items"],"mappings":";;;;;;AAeA;;;;;;;AAOC;AAEM,MAAMA,aAAaC,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,yNAAA,EAOA;MAAAC,UAAA,EAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAWA;;;;;AAKC;AAEM,MAAMC,gBAAgBJ,SAAA,CAAU;AACrC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,8YAAA,EAoBA;MAAAC,UAAA,EAAA,IAAA;AAAAE,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,MAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASA,KAAAA,CAAS,GAAGC,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/film.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { Clip } from './film/clip.js';
|
|
2
|
+
export { clipLanes, clipWindow, resolveClip, resolveClips } from './film/clips.js';
|
|
3
|
+
export { Film } from './film/film.js';
|
|
4
|
+
export { Adjustment, Build, Filter, IFRAME_PICTURE, Light, Look, Mix, SOUND, Set, Sun, Weather, Winter } from './film/graph/adjust.js';
|
|
5
|
+
export { compileGraph } from './film/graph/compile.js';
|
|
6
|
+
export { FilmGraph, VOCABULARY } from './film/graph/host.js';
|
|
7
|
+
export { Eye, Freeze, Attach as GraphAttach, Chapter as GraphChapter, Insert as GraphInsert, GraphNodeComponent, Stamp as GraphStamp, JoinInto, Lineup, Mark, PatchComponent, Sequence, Shot, Sky, Spine, To, Trace, Type, Video, Voice, collectGraph } from './film/graph/nodes.js';
|
|
8
|
+
export { Blend, Blur, Dip, Flash, Iris, JOIN_SECS, Joins, Luma, Melt, PRESENTATIONS, STILL_JOINS, Wipe, inGlass, retire, seamShape } from './film/joins.js';
|
|
9
|
+
export { RAD, clamp01, hex, lerp, luminance, mmss, rgba, smooth } from './film/math.js';
|
|
10
|
+
export { Captions, Insert, Stamp, Track } from './film/overlays.js';
|
|
11
|
+
export { IframePicture } from './film/picture.js';
|
|
12
|
+
export { Plate } from './film/plate.js';
|
|
13
|
+
export { Burst, Menu, Player } from './film/player.js';
|
|
14
|
+
export { Rail } from './film/rail.js';
|
|
15
|
+
export { TICK, beatStart, chapterHeads, contents, cues, joinInto, schedule, secsBefore, tailFor, totalSecs, waypoints } from './film/schedule.js';
|
|
16
|
+
export { Seam } from './film/seam.js';
|
|
17
|
+
export { EndCard, Gate } from './film/titles.js';
|
|
18
|
+
//# sourceMappingURL=film.js.map
|
package/dist/film.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"film.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
|