Benchmarks / CPU and I/O (per commit) (push) Failing after 30s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Failing after 47s
Build and test / Layer separation (push) Successful in 27s
🐳 Android image / Build and push (push) Successful in 3s
Build and test / android-image (push) Successful in 4s
🐳 Windows image / Build and push (push) Successful in 1s
Build and test / windows-image (push) Successful in 1s
Traceability / Requirement traces (push) Failing after 38s
Build and test / Android (aarch64) (push) Failing after 2m24s
Build and test / Windows (x86_64, cross) (push) Failing after 3m21s
docs/manual/README.md is a tour for a photographer opening DarkRoom for the first time — one picture per thing, moving where movement is the point. tools/manual/ is how the pictures are made: drive.py puppeteers the desktop build on a private Xvfb (launch, click, drag, type, screenshot, record), scenes.py is each picture as a script, and record.sh runs them all over a folder and writes the results into docs/manual/media/. The media is in LFS, with the CI pulls excluding it as they exclude the fixtures; a screenshot changes wholesale when the interface does. Nothing in the pictures shows a person, by design: the demo library is seventy urban and alpine frames, chosen from the catalog's rows that face detection found nobody in. The traceability matrix is regenerated here after the rebase that brought this branch up to master.
324 lines
8.8 KiB
Python
Executable File
324 lines
8.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""The manual's scenes: `scenes.py OUT_DIR scene [scene...]`, or `all`.
|
||
|
||
Each scene drives the running app through `drive.py` and leaves a PNG or an
|
||
MP4 in OUT_DIR, named after itself. `record.sh` runs them in order and turns
|
||
the MP4s into GIFs.
|
||
|
||
Coordinates are window pixels for a 1600×1100 window over the manual's own
|
||
library (see record.sh): which cell holds which photograph is part of the
|
||
scene, so a different library needs the numbers looked at again. Panel
|
||
coordinates hold for any library.
|
||
"""
|
||
import os
|
||
import sys
|
||
import time
|
||
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
import drive as dr # noqa: E402
|
||
|
||
OUT = sys.argv[1] if len(sys.argv) > 1 else '.'
|
||
|
||
|
||
def shot(name):
|
||
dr.shot(f'{OUT}/{name}.png')
|
||
|
||
|
||
def rec(name):
|
||
dr.rec_start(f'{OUT}/{name}.mp4')
|
||
|
||
|
||
def cut():
|
||
dr.rec_stop()
|
||
|
||
|
||
def pause(s):
|
||
time.sleep(s)
|
||
|
||
|
||
def hold(px, py, seconds):
|
||
dr.move(px, py)
|
||
pause(0.15)
|
||
dr.x('mousedown', 1)
|
||
pause(seconds)
|
||
dr.x('mouseup', 1)
|
||
|
||
|
||
def wheel(n, px, py):
|
||
dr.move(px, py)
|
||
dr.x('click', '--repeat', abs(n), '--delay', 40, 5 if n > 0 else 4)
|
||
|
||
|
||
def ctrl_wheel(n, px, py):
|
||
dr.move(px, py)
|
||
dr.x('keydown', 'ctrl')
|
||
for _ in range(abs(n)):
|
||
dr.x('click', 5 if n > 0 else 4)
|
||
pause(0.35)
|
||
dr.x('keyup', 'ctrl')
|
||
|
||
|
||
# --- places on the screen ---------------------------------------------------
|
||
LIBRARY = (55, 22) # ‹ Library, in the develop header
|
||
SELECT = (942, 22) # Select / Done, in the library header
|
||
SETTINGS_LIB = (1543, 22)
|
||
SETTINGS_DEV = (1343, 22)
|
||
EXPORT = (720, 22)
|
||
BACK = (60, 22) # ‹ Back, on the settings page
|
||
RAIL = {'photo': (30, 75), 'compose': (30, 130), 'local': (30, 185), 'repair': (30, 240)}
|
||
GROUPS = {'all': 1269, 'optics': 1313, 'light': 1359, 'colour': 1406, 'effects': 1457, 'detail': 1506}
|
||
BEFORE = (210, 1045)
|
||
RESET_ADJUST = (1565, 741) # "reset" in the Adjust heading, All group, scrolled to top
|
||
CELL_TEAPOT = (424, 165)
|
||
CELL_PANO_FIRST = (1264, 165)
|
||
CELL_CHINATOWN = (963, 525)
|
||
CELL_NY_LAST = (1502, 525)
|
||
COLLECTION_ROW = (60, 90)
|
||
|
||
|
||
def group(name):
|
||
dr.click(GROUPS[name], 67)
|
||
pause(0.9)
|
||
|
||
|
||
def to_library():
|
||
dr.click(*LIBRARY)
|
||
pause(2)
|
||
|
||
|
||
def open_chinatown():
|
||
dr.click(*CELL_CHINATOWN)
|
||
pause(6)
|
||
|
||
|
||
def reset_edit():
|
||
group('all')
|
||
wheel(-60, 1420, 700)
|
||
dr.click(*RESET_ADJUST)
|
||
pause(1)
|
||
|
||
|
||
# --- library ----------------------------------------------------------------
|
||
def library():
|
||
shot('library')
|
||
|
||
|
||
def library_rating():
|
||
rec('library-rating')
|
||
dr.move(424, 900); pause(1.0)
|
||
dr.click(444, 939); pause(1.2)
|
||
dr.move(604, 900); pause(0.8)
|
||
dr.click(644, 939); pause(1.2)
|
||
dr.move(800, 700); pause(0.6)
|
||
dr.click(665, 61); pause(1.5) # 3+
|
||
dr.click(384, 61); pause(1.2) # All
|
||
cut()
|
||
|
||
|
||
def library_timeline():
|
||
rec('library-timeline')
|
||
dr.drag(300, 200, 300, 900, 40); pause(1.0)
|
||
dr.drag(300, 900, 300, 150, 40); pause(1.0)
|
||
cut()
|
||
|
||
|
||
def library_thumbsize():
|
||
rec('library-thumbsize')
|
||
ctrl_wheel(-4, 900, 500); pause(0.8)
|
||
ctrl_wheel(4, 900, 500); pause(0.8)
|
||
cut()
|
||
|
||
|
||
def library_selection():
|
||
dr.click(*SELECT); pause(0.5)
|
||
dr.click(*CELL_PANO_FIRST); pause(0.3)
|
||
dr.x('keydown', 'shift'); dr.click(*CELL_NY_LAST); dr.x('keyup', 'shift'); pause(0.8)
|
||
for x in (963, 1143, 1323, 1502):
|
||
dr.x('keydown', 'ctrl'); dr.click(x, 525); dr.x('keyup', 'ctrl'); pause(0.3)
|
||
pause(0.5)
|
||
shot('library-selection')
|
||
|
||
|
||
def library_keywords():
|
||
# Continues from library_selection: twelve frames selected.
|
||
rec('library-keywords')
|
||
dr.click(1117, 1079); pause(1.2)
|
||
for word in ('alps', 'panorama', 'summer'):
|
||
dr.x('type', '--delay', 90, word); dr.x('key', 'Return'); pause(0.7)
|
||
pause(0.8)
|
||
dr.click(800, 673); pause(1.0) # Done
|
||
cut()
|
||
dr.click(*SELECT); pause(0.5) # leave selecting
|
||
|
||
|
||
def library_collections():
|
||
dr.click(212, 22); pause(0.3) # + in the collections header
|
||
dr.x('type', '--delay', 90, 'Alps'); dr.x('key', 'Return'); pause(1.2)
|
||
rec('library-collections')
|
||
dr.drag(*CELL_PANO_FIRST, *COLLECTION_ROW, 40); pause(1.5)
|
||
dr.click(*SELECT); pause(0.5)
|
||
dr.click(424, 350); pause(0.3)
|
||
dr.x('keydown', 'shift'); dr.click(1502, 350); dr.x('keyup', 'shift'); pause(0.8)
|
||
dr.drag(963, 350, *COLLECTION_ROW, 40); pause(1.5)
|
||
dr.click(*SELECT); pause(0.5)
|
||
dr.click(*COLLECTION_ROW); pause(1.5)
|
||
cut()
|
||
shot('library-collection')
|
||
dr.click(60, 48); pause(1.2) # All photographs
|
||
|
||
|
||
# --- develop ----------------------------------------------------------------
|
||
def develop():
|
||
open_chinatown()
|
||
group('all')
|
||
shot('develop')
|
||
|
||
|
||
def develop_groups():
|
||
rec('develop-groups')
|
||
for g in ['optics', 'light', 'colour', 'effects', 'detail', 'all']:
|
||
group(g); pause(0.6)
|
||
cut()
|
||
|
||
|
||
def develop_light():
|
||
group('light')
|
||
rec('develop-light')
|
||
pause(0.5)
|
||
dr.drag(1398, 792, 1422, 792, 25); pause(0.8) # exposure up
|
||
dr.drag(1398, 918, 1320, 918, 25); pause(0.8) # highlights down
|
||
dr.drag(1398, 964, 1450, 964, 25); pause(0.8) # shadows up
|
||
hold(*BEFORE, 1.6); pause(1.0)
|
||
cut()
|
||
reset_edit()
|
||
|
||
|
||
def develop_zoom():
|
||
rec('develop-zoom')
|
||
dr.move(650, 500); pause(0.3)
|
||
dr.x('click', '--repeat', 2, '--delay', 80, 1); pause(1.5)
|
||
dr.drag(650, 500, 900, 700, 30); pause(0.8)
|
||
dr.drag(900, 700, 500, 450, 30); pause(0.8)
|
||
dr.x('click', '--repeat', 2, '--delay', 80, 1); pause(1.2)
|
||
cut()
|
||
|
||
|
||
def develop_wb():
|
||
group('colour')
|
||
rec('develop-wb')
|
||
pause(0.4)
|
||
dr.click(1542, 767); pause(0.8) # pick
|
||
dr.click(1000, 300); pause(1.5) # a neutral wall
|
||
hold(*BEFORE, 1.4); pause(0.8)
|
||
cut()
|
||
reset_edit()
|
||
|
||
|
||
def compose():
|
||
dr.click(*RAIL['compose']); pause(1.2)
|
||
rec('compose')
|
||
pause(0.4)
|
||
dr.drag(66, 182, 260, 330, 30); pause(0.8)
|
||
dr.drag(1236, 964, 1100, 900, 25); pause(0.8)
|
||
dr.drag(1420, 593, 1448, 593, 20); pause(1.0) # straighten
|
||
dr.click(1466, 647); pause(1.2) # 1:1
|
||
dr.click(1378, 647); pause(1.0) # Original
|
||
dr.click(132, 1045); pause(1.2) # Done composing
|
||
cut()
|
||
shot('compose-done')
|
||
dr.click(1567, 510); pause(1.0) # reset compose
|
||
dr.click(115, 1045); pause(1.0) # refit
|
||
|
||
|
||
def local_segment():
|
||
dr.click(*RAIL['local']); pause(1.2)
|
||
dr.click(1420, 435); pause(14) # Find subjects
|
||
shot('local-categories')
|
||
dr.click(1266, 646); pause(2.5) # Sky
|
||
shot('local-segment')
|
||
|
||
|
||
def local_paint():
|
||
# Continues from local_segment: the sky mask selected and shown.
|
||
rec('local-paint')
|
||
wheel(12, 1420, 800); pause(0.8)
|
||
shot('local-mask-row')
|
||
cut()
|
||
|
||
|
||
def local_done():
|
||
dr.click(62, 1045); pause(1.0) # Done masking
|
||
reset_edit()
|
||
|
||
|
||
def repair():
|
||
dr.click(*RAIL['repair']); pause(1.2)
|
||
rec('repair')
|
||
pause(0.4)
|
||
dr.click(700, 620); pause(3)
|
||
dr.drag(1264, 460, 1330, 460, 20); pause(1.5) # size
|
||
hold(*BEFORE, 1.4); pause(0.8)
|
||
cut()
|
||
dr.click(125, 1045); pause(1.0) # Done repairing
|
||
reset_edit()
|
||
|
||
|
||
def film():
|
||
group('all')
|
||
rec('film')
|
||
dr.click(1420, 779); pause(1.5) # Film ▸
|
||
dr.click(1420, 953); pause(3) # Velvia 100
|
||
hold(*BEFORE, 1.4); pause(0.8)
|
||
cut()
|
||
reset_edit()
|
||
|
||
|
||
def presets():
|
||
dr.click(1420, 692); pause(1.5)
|
||
shot('presets')
|
||
dr.click(800, 783); pause(1.0) # Done
|
||
|
||
|
||
def develop_export():
|
||
dr.click(*EXPORT); pause(8)
|
||
shot('export-done')
|
||
|
||
|
||
# --- panorama ---------------------------------------------------------------
|
||
def panorama():
|
||
to_library()
|
||
library_selection()
|
||
rec('panorama')
|
||
dr.click(1325, 1079); pause(40) # Merge to panorama
|
||
dr.click(184, 637); pause(45) # Fill the border
|
||
dr.click(1543, 22); pause(60) # Merge
|
||
cut()
|
||
shot('panorama-done')
|
||
dr.click(*BACK); pause(2)
|
||
|
||
|
||
# --- settings ---------------------------------------------------------------
|
||
def settings():
|
||
dr.click(*SETTINGS_LIB); pause(2)
|
||
shot('settings')
|
||
wheel(30, 700, 600); pause(1)
|
||
shot('settings-export')
|
||
dr.click(*BACK); pause(1.5)
|
||
|
||
|
||
ALL = [
|
||
'library', 'library_rating', 'library_timeline', 'library_thumbsize',
|
||
'library_selection', 'library_keywords', 'library_collections',
|
||
'develop', 'develop_groups', 'develop_light', 'develop_zoom', 'develop_wb',
|
||
'compose', 'local_segment', 'local_paint', 'local_done', 'repair', 'film',
|
||
'presets', 'panorama', 'settings',
|
||
]
|
||
|
||
if __name__ == '__main__':
|
||
names = sys.argv[2:]
|
||
if names == ['all']:
|
||
names = ALL
|
||
for name in names:
|
||
print(f'-- {name}', flush=True)
|
||
globals()[name]()
|