nlve/tests/dropgap.sh
4aiman b078700fc7 Прилипание сильнее кадровой сетки: наезда в семь миллисекунд больше нет
Клип прилипал к концу соседа, а потом округлялся к ближайшему кадру — и
уезжал с той самой границы, к которой только что прилип. Границы клипов
в монтаже лежат не на сетке: они приходят из исходников. Влево это наезд
в семь миллисекунд: ноль пикселей на экране, а в модели заказ на переход.
Человек ищет, откуда взялся кроссфейд там, где ничего не накладывается.

Замерено: дыра, раздвинутая ровно под клип, давала после перетаскивания
наезд 0.007383 с слева и дыру такого же размера справа.

Теперь решение одно и принимается один раз: прилипло — ставим ровно
туда, не прилипло — на ближайший кадр (PlaceTime). Тем же путём идут и
оба растягивания за край.

Схлопывание больше не округляет каждого соседа по отдельности: все едут
на точную величину сдвига, и расстояния между ними остаются как были.
Иначе связка разъезжалась — картинка садилась на границу, а её звук
уезжал на полкадра.

Сообщение о наезде меряет той же мерой, что и сборка: кусок короче
половины кадра она пропускает, и пугать переходом из него незачем.

Проверка tests/dropgap.sh, на прежней сборке падает.
2026-10-16 00:24:18 +03:00

133 lines
6 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# Клип, брошенный в дыру ровно по своему размеру, встаёт в неё без
# наезда.
#
# Границы клипов в монтаже лежат не на кадровой сетке: они приходят из
# исходников. Клип прилипал к концу соседа, а потом округлялся к
# ближайшему кадру — и уезжал с этой границы на полкадра. Влево это
# наезд в семь миллисекунд: на экране ноль пикселей, а в модели заказ на
# переход, и человек ищет, откуда взялся кроссфейд там, где ничего не
# накладывается.
#
# Монтаж строится числами: клип A с концом не на сетке, дыра ровно по
# длине клипа D, сам D в конце. D тащится в дыру. Результат читается из
# сохранённого проекта.
#
# Запуск (экран :99 поднят):
# tests/dropgap.sh [<путь к nlve>]
set -e
cd "$(dirname "$0")/.."
BIN=${1:-$PWD/nlve}
DISP=${NLVE_DISPLAY:-:99}
SRC=$PWD/test/поток/пара.nlve
PROJ=$PWD/test/поток/дыра-ровно.nlve
[ -f "$SRC" ] || { echo "нет монтажа $SRC"; exit 2; }
export DISPLAY=$DISP
reachable() { timeout 5 xdotool getdisplaygeometry >/dev/null 2>&1; }
if ! reachable; then
for p in $(pgrep -x Xvfb); do
A=$(tr '\0' '\n' < /proc/$p/cmdline | grep -A1 -x -- -auth | tail -1)
[ -f "$A" ] || continue
XAUTHORITY=$A; export XAUTHORITY
reachable && break
done
fi
reachable ||
{ echo "нет доступа к экрану $DISP: подними его (xvfb-run) и повтори"; exit 2; }
python3 - "$SRC" "$PROJ" <<'PY'
import json, sys, copy
d = json.load(open(sys.argv[1]))
V = next(t for t in d['tracks'] if t['name'] == 'V1')
A = next(t for t in d['tracks'] if t['name'] == 'A1')
ов, оа = copy.deepcopy(V['clips'][0]), copy.deepcopy(A['clips'][0])
def клип(обр, ид, группа, старт, длина, вход):
c = copy.deepcopy(обр)
c.update(id=ид, group=группа, tl_start=старт, duration=длина,
src_in=вход, fade_in=0.0, fade_out=0.0)
return c
шаг = 1001/60000
D = round(3.5/шаг)*шаг # длина клипа — ровно по кадрам
конецA = 10.000700 # а граница соседа — нарочно между кадрами
V['clips'] = [клип(ов, 'a_v', 'ga', 0.0, конецA, 100.0),
клип(ов, 'b_v', 'gb', конецA + D, 10.0001, 200.0),
клип(ов, 'd_v', 'gd', 30.0, D, 300.0)]
A['clips'] = [клип(оа, 'a_a', 'ga', 0.0, конецA, 100.0),
клип(оа, 'b_a', 'gb', конецA + D, 10.0001, 200.0),
клип(оа, 'd_a', 'gd', 30.0, D, 300.0)]
json.dump(d, open(sys.argv[2], 'w'), ensure_ascii=False)
print(' дыра %.6f..%.6f, клип длиной %.6f с — ровно по размеру'
% (конецA, конецA + D, D))
PY
for p in $(pgrep -x nlve); do
tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p
done
sleep 1
SP=$PWD/test/поток/профиль-дыра-ровно
rm -rf "$SP"; mkdir -p "$SP"
rm -f "$PROJ.autosave"
XDG_CONFIG_HOME=$SP XDG_CACHE_HOME=$SP XDG_DATA_HOME=$SP \
setsid "$BIN" "$PROJ" >/dev/null 2>&1 < /dev/null &
sleep 14
X="timeout 8 xdotool"
WID=
for w in $($X search --name "дыра-ровно.nlve" 2>/dev/null); do
xwininfo -id "$w" 2>/dev/null | grep -q IsViewable && { WID=$w; break; }
done
[ -n "$WID" ] || { echo "живое окно редактора не найдено"; exit 2; }
$X windowmove "$WID" 0 0 windowsize "$WID" 1270 990
sleep 2
ROW=${NLVE_ROW:-505}
# Два щелчка колесом от себя: в окно должен попасть и конец монтажа, где
# лежит перетаскиваемый клип, и дыра.
$X mousemove 700 $ROW
$X keydown ctrl click 5 keyup ctrl; sleep 0.4
$X keydown ctrl click 5 keyup ctrl; sleep 1
# Тащим клип с конца к левому краю дыры. Целимся грубо: доводить должно
# прилипание, в том и смысл.
$X mousemove 1200 $ROW mousedown 1; sleep 0.4
$X mousemove 1000 $ROW; sleep 0.3
$X mousemove 850 $ROW; sleep 0.3
$X mousemove 815 $ROW; sleep 0.5
$X mouseup 1; sleep 1.5
$X key ctrl+s; sleep 2
for p in $(pgrep -x nlve); do
tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p
done
rm -f "$PROJ.autosave"
python3 - "$PROJ" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
плохо = 0
for t in d['tracks']:
кл = sorted(t['clips'], key=lambda c: c['tl_start'])
if len(кл) < 3:
continue
for a, b in zip(кл, кл[1:]):
зазор = b['tl_start'] - (a['tl_start'] + a['duration'])
# Спрос строгий, до микросекунд: прилипание на то и прилипание,
# чтобы край сел ровно на край. Промах в семь миллисекунд меньше
# половины кадра и в ролике ничего не меняет, но монтаж от этого
# перестаёт быть встык: наезд рисуется, пишется в строку
# состояния и пугает переходом, которого не просили.
if abs(зазор) > 1e-6:
print(' ПЛОХО: на %s между %s и %s %s %.6f с'
% (t['name'], a['id'], b['id'],
'наезд' if зазор < 0 else 'дыра', abs(зазор)))
плохо = 1
print(' %s: %s' % (t['name'],
' '.join('%s %.6f' % (c['id'], c['tl_start']) for c in кл)))
if плохо:
raise SystemExit(1)
print(' ok: клип сел в дыру вплотную, ни наезда, ни остатка')
PY