+12








fcd7c979df
Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> Co-authored-by: arturict <arturict@users.noreply.github.com> Co-authored-by: Thomas <thomas@ajnart.fr> Co-authored-by: Afonso Jorge Ramos <afonsojorgeramos@gmail.com> Co-authored-by: Danura30082 <danura30082+github@gmail.com> Co-authored-by: Kushie <122986913+kushiemoon-dev@users.noreply.github.com> Co-authored-by: ajnart <thomas@ajnart.fr> Co-authored-by: ajnart <thomas@ajnart.dev> Co-authored-by: chenjun <chenjun@foxmail.com> Co-authored-by: Wikibear <wikibear@users.noreply.github.com> Co-authored-by: Bergasha <bergasha@gmail.com> Co-authored-by: leafyelin <43306196+leafyelin@users.noreply.github.com> Co-authored-by: Lucas Araujo <lucas.lra@gmail.com> Co-authored-by: GreenMonito <86085570+GreenMonito@users.noreply.github.com> Co-authored-by: Nerdy-Technician <roffo@nerdy-technician.social> Co-authored-by: Jun <34545896+jcstrong@users.noreply.github.com> Co-authored-by: Edmond Abraham <54119952+gitedmond@users.noreply.github.com> Co-authored-by: thrawnx <tight.bulb9384@fastmail.com> Co-authored-by: Chris <58918656+nordada@users.noreply.github.com> Co-authored-by: yi111 <153097222+Yi-111-a@users.noreply.github.com> Co-authored-by: Yanis <naamaneyanis@gmail.com> Co-authored-by: Wikibear <me@sej.de> Co-authored-by: alessiottavo <93441886+alessiottavo@users.noreply.github.com>
104 lines
5.4 KiB
Python
104 lines
5.4 KiB
Python
"""Readable-cut time remaps: output time u -> reel time t.
|
|
|
|
Each shot keeps transitions and actions near normal speed, then slows to a drift once its
|
|
content is fully on screen. Shot lengths are whole beats at 128 BPM, so cuts stay on the grid.
|
|
Segment speeds: a number = fixed speed, ('H', w) = hold (solved so the shot fills its beats).
|
|
"""
|
|
import json
|
|
import sys
|
|
|
|
import numpy as np
|
|
|
|
BEAT = 60 / 128
|
|
B = BAR = 4 * BEAT
|
|
SLAM = 7 * B + 2 * BEAT
|
|
H = lambda w=1: ('H', w)
|
|
|
|
FIRST_HALF = [
|
|
('S1 logo', 5, [(0, 0.9375, 0.9), (0.9375, 1.45, 0.75), (1.45, 1.52, H()), (1.52, B, 1.0)]),
|
|
('S2 services', 8, [(B, 2 * B, H())]), # one card per beat
|
|
('S3 one place', 5, [(2 * B, 4.3, 0.85), (4.3, 4.78, H()), (4.78, 3 * B, 0.8)]),
|
|
('S4 drag & drop', 5, [(3 * B, 7.12, 0.75), (7.12, 7.26, H()), (7.26, 4 * B, 1.0)]),
|
|
('S5 live data', 6, [(4 * B, 8.36, 0.75), (8.36, 9.15, 0.8), (9.15, 9.26, H()), (9.26, 5 * B, 1.0)]),
|
|
('S6 integrations', 5, [(5 * B, 10.4, 0.8), (10.4, 10.98, H()), (10.98, 6 * B, 1.0)]),
|
|
('S7 features', 6, [(6 * B, 11.45, 1.0), (11.45, 12.7, 0.65), (12.7, 12.88, H()), (12.88, 7 * B, 0.9)]),
|
|
]
|
|
FA = 11 * B # front door, diagram
|
|
FB = 13 * B # front door, live demo
|
|
V2_HALF = [
|
|
('S8 now in v2', 6, [(7 * B, SLAM, 2 / 3), (SLAM, 14.5, 0.8), (14.5, 14.8, H()), (14.8, 8 * B, 1.0)]),
|
|
('S9 custom widgets', 6, [(8 * B, 15.2, 1.0), (15.2, 16.3, 0.6), (16.3, 16.675, H()), (16.675, 9 * B, 1.0)]),
|
|
('S10 workshop', 6, [(9 * B, 17.075, 1.0), (17.075, 18.2, 0.65), (18.2, 18.55, H()), (18.55, 10 * B, 1.0)]),
|
|
('S11 assistant', 7, [(10 * B, 18.95, 1.0), (18.95, 20.42, 0.55), (20.42, 20.525, H()), (20.525, 11 * B, 1.0)]),
|
|
('FDA endpoint diagram', 16, [(FA, FA + 0.3, 1.0), (FA + 0.3, FA + 0.95, 0.6), (FA + 0.95, FA + 0.96, H(1)), (FA + 0.96, FA + 2.7, 0.45),
|
|
(FA + 2.7, FA + 3.52, 0.7), (FA + 3.52, FA + 3.55, H(1.5)), (FA + 3.55, 13 * B, 1.0)]),
|
|
('FDB ntfy demo', 16, [(FB, FB + 0.3, 1.0), (FB + 0.3, FB + 1.0, 0.45), (FB + 1.0, FB + 1.9, 0.5), (FB + 1.9, FB + 3.1, 0.5),
|
|
(FB + 3.1, FB + 3.55, H()), (FB + 3.55, 15 * B, 1.0)]),
|
|
('S12 board editing', 7, [(15 * B, 28.325, 1.0), (28.325, 28.75, 0.65), (28.75, 28.965, H(1)), (28.965, 29.605, 0.65), (29.605, 29.8, H(2)), (29.8, 16 * B, 1.0)]),
|
|
('S13 setup studio', 6, [(16 * B, 30.2, 1.0), (30.2, 31.47, 0.65), (31.47, 31.675, H()), (31.675, 17 * B, 1.0)]),
|
|
('S14 +31', 6, [(17 * B, 33.45, 0.7), (33.45, 33.55, H()), (33.55, 18 * B, 1.0)]),
|
|
('S15 also in v2', 7, [(18 * B, 33.95, 1.0), (33.95, 35.37, 0.65), (35.37, 35.425, H()), (35.425, 19 * B, 0.8)]),
|
|
('S16 end card', 8, [(19 * B, 36.4, 0.6), (36.4, 37.03, H()), (37.03, 20 * B, 1.0)]),
|
|
]
|
|
|
|
DU = 1 / 480
|
|
|
|
|
|
def build(scenes):
|
|
segs, shots, u = [], [], 0.0
|
|
for name, beats, parts in scenes:
|
|
D = beats * BEAT
|
|
fixed = sum((b - a) / s for a, b, s in parts if not isinstance(s, tuple))
|
|
weights = sum(s[1] for a, b, s in parts if isinstance(s, tuple))
|
|
hold = D - fixed
|
|
assert hold > 0.05, (name, hold)
|
|
u0 = u
|
|
for a, b, s in parts:
|
|
dur = hold * s[1] / weights if isinstance(s, tuple) else (b - a) / s
|
|
segs.append((u, u + dur, (b - a) / dur))
|
|
u += dur
|
|
shots.append({'name': name, 'u0': u0, 'u1': u, 't0': parts[0][0], 't1': parts[-1][1], 'beats': beats})
|
|
n = int(round(u / DU))
|
|
uc = (np.arange(n) + 0.5) * DU
|
|
speed = np.zeros(n)
|
|
for a, b, s in segs:
|
|
speed[(uc >= a) & (uc < b)] = s
|
|
# soften speed changes (no visible lurches), then re-fit each shot to its exact reel span
|
|
k = np.exp(-0.5 * (np.arange(-60, 61) * DU / 0.06) ** 2)
|
|
speed = np.convolve(np.pad(speed, 60, mode='edge'), k / k.sum(), mode='valid')
|
|
for sh in shots:
|
|
i0, i1 = int(round(sh['u0'] / DU)), int(round(sh['u1'] / DU))
|
|
speed[i0:i1] *= (sh['t1'] - sh['t0']) / (speed[i0:i1].sum() * DU)
|
|
t = scenes[0][2][0][0] + np.concatenate([[0.0], np.cumsum(speed) * DU])
|
|
return {'du': DU, 'duration': n * DU, 't': np.round(t, 6).tolist(), 'shots': shots}
|
|
|
|
|
|
PRE = 56 / 60 # v2 cut: black pre-roll (whole frames) with a riser into the "Now in v2" impact
|
|
|
|
|
|
def join(a, b):
|
|
"""Concatenate two warps (a ends exactly where b starts, in both reel and output time)."""
|
|
off = a['duration']
|
|
assert abs(a['t'][-1] - b['t'][0]) < 1e-6
|
|
return {'du': DU, 'duration': off + b['duration'], 't': a['t'] + b['t'][1:],
|
|
'shots': a['shots'] + [{**s, 'u0': s['u0'] + off, 'u1': s['u1'] + off} for s in b['shots']]}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
A, Bw = build(FIRST_HALF), build(V2_HALF)
|
|
assert round(A['duration'] * 60, 6) == int(A['duration'] * 60), 'first half must be whole frames'
|
|
json.dump(A, open('warp-a.json', 'w'))
|
|
json.dump(Bw, open('warp-b.json', 'w'))
|
|
G = join(A, Bw)
|
|
json.dump(G, open('warp-global.json', 'w'))
|
|
n = int(round(PRE / DU))
|
|
C = {'du': DU, 'duration': PRE + Bw['duration'], 't': [Bw['t'][0]] * n + Bw['t'], 'pre': PRE,
|
|
'shots': [{**s, 'u0': s['u0'] + PRE, 'u1': s['u1'] + PRE} for s in Bw['shots']]}
|
|
json.dump(C, open('warp-v2cut.json', 'w'))
|
|
for name, w in (('a', A), ('b', Bw), ('global', G), ('v2cut', C)):
|
|
print(name, f"{w['duration']:.3f}s", f"{w['duration'] * 60:.2f} frames", sum(s['beats'] for s in w['shots']), 'beats')
|
|
for s in G['shots']:
|
|
ti = G['t'][int(round(s['u1'] / DU))]
|
|
print(f" {s['name']:22s} u {s['u0']:7.3f}-{s['u1']:7.3f} t_end {ti:7.3f} (want {s['t1']:.3f})")
|
|
sys.exit(0)
|