make_baseline_video.pygolden artifact
#!/usr/bin/env python3
"""
Builds baseline_check.mp4 — the office-follow-along video for the baseline check.
Source of truth: Abe's book pages (IMG_4638, IMG_4639), the setup photo (IMG_4452),
and Abe's own mail + calendar (why the S6 went in; how the baseline was established).
Everything on screen is in FEET to three places. No millimetres.
To swap in Abe's own quiz page: edit the QUIZ block near the bottom and re-run:
python3 make_baseline_video.py
"""
import os
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import imageio.v2 as imageio
IN = "/app/inputs"
OUT = "/app/outputs"
FPS = 30
W, H = 1920, 1080
# ---- palette -------------------------------------------------------------
BG = (22, 24, 28)
PANEL = (32, 35, 41)
INK = (238, 240, 243)
MUTED = (154, 160, 170)
GOOD = (60, 196, 124)
BAD = (230, 88, 80)
ACCENT = (247, 197, 42) # instrument yellow
LINE = (72, 76, 86)
GOODBG = (26, 46, 36)
BADBG = (52, 30, 30)
FONTDIR = "/usr/share/fonts/truetype/dejavu"
_cache = {}
def font(kind, size):
key = (kind, size)
if key not in _cache:
f = {"sans":"DejaVuSans.ttf","bold":"DejaVuSans-Bold.ttf",
"mono":"DejaVuSansMono.ttf","monob":"DejaVuSansMono-Bold.ttf"}[kind]
_cache[key] = ImageFont.truetype(os.path.join(FONTDIR, f), size)
return _cache[key]
# ---- draw helpers --------------------------------------------------------
def canvas(bg=BG):
im = Image.new("RGB", (W, H), bg)
return im, ImageDraw.Draw(im)
def text(d, xy, s, fnt, fill=INK, anchor="la"):
d.text(xy, s, font=fnt, fill=fill, anchor=anchor)
def measure(fnt, s):
return fnt.getlength(s)
def wrap(d, s, fnt, x, y, maxw, fill=INK, leading=1.35, center=False):
words, lines, cur = s.split(), [], ""
for w in words:
t = (cur + " " + w).strip()
if measure(fnt, t) <= maxw:
cur = t
else:
if cur: lines.append(cur)
cur = w
if cur: lines.append(cur)
lh = int(fnt.size * leading)
for i, ln in enumerate(lines):
d.text((x, y+i*lh), ln, font=fnt, fill=fill, anchor=("ma" if center else "la"))
return y + len(lines)*lh
def panel(d, box, fill=PANEL, outline=None, width=2, radius=22):
d.rounded_rectangle(box, radius=radius, fill=fill, outline=outline, width=width)
def chip(d, xy, s, fill, textcol=(15,16,18), fnt=None, pad=(22,12)):
fnt = fnt or font("bold", 34)
tw = measure(fnt, s); x, y = xy
box = (x, y, x+tw+pad[0]*2, y+fnt.size+pad[1]*2)
d.rounded_rectangle(box, radius=(box[3]-box[1])//2, fill=fill)
d.text((x+pad[0], y+pad[1]-2), s, font=fnt, fill=textcol)
return box
def label_masked(d, cx, cy, s, fnt, fill, pad=12, bg=BG):
"""Center a label at (cx, cy top) with a background box so any line behind it stays readable."""
w = measure(fnt, s)
d.rectangle((cx-w/2-pad, cy-4, cx+w/2+pad, cy+fnt.size+8), fill=bg)
d.text((cx, cy), s, font=fnt, fill=fill, anchor="ma")
def footer(d, page=""):
d.line((90, H-92, W-90, H-92), fill=LINE, width=2)
text(d, (90, H-70),
"S6 SN 93441234 • Baseline BL-1–BL-2 = 812.000 ft • feet to three places",
font("sans", 26), MUTED)
if page:
text(d, (W-90, H-70), page, font("bold", 26), MUTED, anchor="ra")
def header(d, kicker, title, tcol=INK):
text(d, (90, 66), kicker, font("bold", 30), ACCENT)
# auto-fit title so it never clips the right edge
size = 72
while size > 50 and measure(font("bold", size), title) > (W-180):
size -= 2
text(d, (90, 114), title, font("bold", size), tcol)
d.line((90, 210, W-90, 210), fill=LINE, width=3)
def goodbad_headers(d, lx, rx, top, w, badlabel="BAD"):
d.rounded_rectangle((lx, top, lx+w, top+70), radius=14, fill=GOOD)
d.rounded_rectangle((rx, top, rx+w, top+70), radius=14, fill=BAD)
text(d, (lx+w//2, top+8), "GOOD", font("bold", 44), (12,30,20), anchor="ma")
text(d, (rx+w//2, top+8), badlabel, font("bold", 44), (30,12,12), anchor="ma")
def load_fit(path, box_w, box_h):
im = Image.open(path).convert("RGB")
r = min(box_w/im.width, box_h/im.height)
return im.resize((max(1,int(im.width*r)), max(1,int(im.height*r))), Image.LANCZOS)
SLIDES = []
def add(im, sec, fade=True): SLIDES.append((im, sec, fade))
# =========================================================================
# 0. TITLE
# =========================================================================
im, d = canvas()
d.rectangle((0,0,W,12), fill=ACCENT)
text(d, (W//2, 235), "BASELINE CHECK", font("bold", 128), INK, anchor="ma")
text(d, (W//2, 388), "The whole thing, start to finish — so anyone can run it without Abe.",
font("sans", 40), MUTED, anchor="ma")
panel(d, (W//2-580, 500, W//2+580, 770), fill=PANEL, outline=LINE)
text(d, (W//2, 528), "Any instrument back from a bench gets this before it goes out.",
font("sans", 36), INK, anchor="ma")
text(d, (W//2, 596), "Tripod both ends. PPM every time. Feet to three places.",
font("bold", 40), INK, anchor="ma")
text(d, (W//2, 664), "Inside 0.010 ft = good. Over = it goes back.",
font("bold", 42), ACCENT, anchor="ma")
text(d, (W//2, 720), "Write it down.", font("bold", 36), INK, anchor="ma")
text(d, (W//2, 870), "Do the math on the screen, not in a spreadsheet after.",
font("sans", 34), MUTED, anchor="ma")
add(im, 6.0)
# =========================================================================
# 1. WHERE THE BASELINE CAME FROM
# =========================================================================
im, d = canvas()
header(d, "FIRST — WHAT YOU ARE STANDING ON", "Where our baseline came from")
wrap(d, "Abe set two brass caps behind the shop at 8710 Bowie in 2016 — 812.000 ft apart, with "
"the midpoint 406.000 out. He re-observes the line every spring to hold the 812.000. It is "
"our own baseline, and that 812.000 is the truth we judge against.",
font("sans", 38), 90, 244, W-180, MUTED)
y = 590
x1, x2 = 220, W-220
xm = (x1+x2)//2
d.line((x1, y, x2, y), fill=INK, width=6)
for x, lab, sub in [(x1,"BL-1","0.000"),(xm,"BL-M","406.000"),(x2,"BL-2","812.000")]:
d.line((x, y-34, x, y+34), fill=ACCENT, width=8)
text(d, (x, y-96), lab, font("bold", 46), INK, anchor="ma")
text(d, (x, y+52), sub, font("mono", 34), MUTED, anchor="ma")
d.line((x1, y+150, xm, y+150), fill=LINE, width=3)
d.line((xm, y+150, x2, y+150), fill=LINE, width=3)
label_masked(d, (x1+xm)//2, y+134, "406.000", font("mono", 30), MUTED)
label_masked(d, (xm+x2)//2, y+134, "406.000", font("mono", 30), MUTED)
d.line((x1, y+238, x2, y+238), fill=ACCENT, width=4)
label_masked(d, xm, y+214, "812.000 ft — our hold since 2016", font("bold", 40), ACCENT)
text(d, (xm, y+292), "Two brass caps, set 2016 · re-observed every spring · NOT the brochure.",
font("sans", 30), INK, anchor="ma")
footer(d, "1 / 9")
add(im, 9.0)
# =========================================================================
# 2. WHY THE S6 WENT IN (the story)
# =========================================================================
im, d = canvas()
header(d, "WHY THE S6 WENT IN", "What happened on 26-004")
steps = [
("2 Jun", "Deaf Smith County — the 640-acre pivot as-builts, job 26-004. Estefana on the rod."),
("", "The S6 lost the tracker at 2:20 and would not re-acquire. We finished the day on the R12i and a 100-ft tape."),
("3 Jun", "Drove it to Llano Estacado Instrument Service in Lubbock. Talmadge Ruff opened it up: tracker board and Y servo. Six weeks on the bench."),
("16 Jul","Back four days early, sheet in the case."),
]
yy = 250
for when, what in steps:
if when:
chip(d, (90, yy), when, ACCENT, fnt=font("bold", 30))
yy2 = wrap(d, what, font("sans", 38), 330, yy+6, W-420, INK)
yy = max(yy2, yy+90) + 14
panel(d, (90, 800, W-90, 900), fill=(44,38,24), outline=ACCENT, width=2, radius=16)
text(d, (W//2, 820), "A bench can say it's fixed. The baseline is how WE know —",
font("bold", 36), ACCENT, anchor="ma")
text(d, (W//2, 862), "before it goes back out on a job.", font("bold", 36), ACCENT, anchor="ma")
footer(d, "2 / 9")
add(im, 11.0)
# =========================================================================
# 3. SETUP (good vs bad) — center, level, both ends, constant
# =========================================================================
im, d = canvas()
header(d, "SET UP THE SAME WAY EVERY TIME", "Set up both ends")
photo = load_fit(os.path.join(IN, "IMG_4452.jpg"), 820, 600)
im.paste(photo, (90, 252))
text(d, (90, 252+photo.height+10), "Abe's setup behind the shop.", font("sans", 26), MUTED)
gx, gw = 960, 880
bf = font("sans", 30)
d.rounded_rectangle((gx, 250, gx+gw, 318), radius=14, fill=GOOD)
text(d, (gx+gw//2, 258), "GOOD", font("bold", 40), (12,30,20), anchor="ma")
panel(d, (gx, 326, gx+gw, 600), fill=GOODBG, outline=GOOD, width=2)
for i,s in enumerate([
"Instrument on a tripod over BL-1, legs firm.",
"Center over the cap, then level.",
"Prism on a tripod over BL-2, both level.",
"Trimble 360 prism, constant 0.0."]):
text(d, (gx+30, 352+i*60), "• "+s, bf, INK)
d.rounded_rectangle((gx, 620, gx+gw, 688), radius=14, fill=BAD)
text(d, (gx+gw//2, 628), "BAD", font("bold", 40), (30,12,12), anchor="ma")
panel(d, (gx, 696, gx+gw, 966), fill=BADBG, outline=BAD, width=2)
for i,s in enumerate([
"Prism on a hand pole — it rocks.",
"Legs loose — the tripod walks.",
"Off-center or off-level — reads off.",
"Constant wrong — same bias every shot."]):
text(d, (gx+30, 722+i*60), "• "+s, bf, INK)
footer(d, "3 / 9")
add(im, 10.0)
# =========================================================================
# 4. STEP 1 - PPM (title short; arithmetic clear of footer)
# =========================================================================
im, d = canvas()
header(d, "STEP 1 — BEFORE THE FIRST SHOT, EVERY TIME", "PPM off the shop gauge")
wrap(d, "Read the shop gauge. Punch temperature and pressure into the S6 so it sets PPM. "
"Forget it and the S6 reads short.",
font("sans", 38), 90, 244, W-180, MUTED)
lx, rx, w, top = 120, 1030, 760, 388
goodbad_headers(d, lx, rx, top, w)
panel(d, (lx, top+80, lx+w, top+360), fill=GOODBG, outline=GOOD, width=2)
panel(d, (rx, top+80, rx+w, top+360), fill=BADBG, outline=BAD, width=2)
text(d, (lx+w//2, top+104), "97°F 26.40 inHg → PPM +52", font("mono", 32), INK, anchor="ma")
text(d, (lx+w//2, top+170), "812.005", font("monob", 90), GOOD, anchor="ma")
text(d, (lx+w//2, top+300), "PPM set. Reads true.", font("sans", 32), INK, anchor="ma")
text(d, (rx+w//2, top+104), "PPM left at 0 (forgot it)", font("mono", 32), INK, anchor="ma")
text(d, (rx+w//2, top+170), "811.962", font("monob", 90), BAD, anchor="ma")
text(d, (rx+w//2, top+300), "Struck in July. Set PPM, shot again.", font("sans", 30), INK, anchor="ma")
ay = 838 # arithmetic strip well above footer line at H-92 (=988)
panel(d, (120, ay, W-120, ay+110), fill=PANEL, outline=LINE)
text(d, (150, ay+16), "On the screen:", font("bold", 28), ACCENT)
text(d, (150, ay+56), "812.000 ft × 0.000052 = 0.042 ft short 812.005 − 811.962 = 0.043 ft",
font("mono", 34), INK)
footer(d, "4 / 9")
add(im, 9.0)
# =========================================================================
# 5. STEP 2 - THREE SETS + spread rule (good vs bad)
# =========================================================================
im, d = canvas()
header(d, "STEP 2", "Three sets, HD, feet — check the spread")
wrap(d, "Shoot three sets of horizontal distance. A set more than 0.005 off the other two is a "
"strike (a bump, or PPM). Strike it, shoot it again. Never average a strike.",
font("sans", 38), 90, 244, W-180, MUTED)
lx, rx, w, top = 120, 1030, 760, 440
goodbad_headers(d, lx, rx, top, w)
panel(d, (lx, top+80, lx+w, top+460), fill=GOODBG, outline=GOOD, width=2)
panel(d, (rx, top+80, rx+w, top+460), fill=BADBG, outline=BAD, width=2)
for i,(lab,val) in enumerate([("Set 1","812.005"),("Set 2","812.003"),("Set 3","812.004")]):
text(d, (lx+40, top+108+i*70), lab, font("sans", 36), MUTED)
text(d, (lx+300, top+108+i*70), val, font("mono", 46), INK)
text(d, (lx+40, top+328), "spread = 0.002 (≤ 0.005)", font("mono", 34), GOOD)
text(d, (lx+40, top+388), "Keep all three.", font("bold", 36), GOOD)
for i,(lab,val,c) in enumerate([("Set 1","812.004",INK),("Set 2","812.037",BAD),("Set 3","812.003",INK)]):
text(d, (rx+40, top+108+i*70), lab, font("sans", 36), MUTED)
text(d, (rx+300, top+108+i*70), val, font("mono", 46), c)
text(d, (rx+40, top+328), "Set 2 is 0.033 off (> 0.005)", font("mono", 34), BAD)
text(d, (rx+40, top+388), "Strike Set 2. Reshoot. Don't average.", font("bold", 30), BAD)
footer(d, "5 / 9")
add(im, 9.0)
# =========================================================================
# 6. STEP 3 - MEAN & CALL (good vs bad)
# =========================================================================
im, d = canvas()
header(d, "STEP 3", "Mean of three, less 812.000 — make the call")
wrap(d, "Add the three good sets, divide by three, subtract 812.000. Inside 0.010 ft is good. "
"Over, it goes back. Tight sets can still fail — do the math.",
font("sans", 38), 90, 244, W-180, MUTED)
lx, rx, w, top = 120, 1030, 760, 430
goodbad_headers(d, lx, rx, top, w)
panel(d, (lx, top+80, lx+w, top+470), fill=GOODBG, outline=GOOD, width=2)
panel(d, (rx, top+80, rx+w, top+470), fill=BADBG, outline=BAD, width=2)
text(d, (lx+40, top+96), "S6 · 16 Jul · after repair", font("sans", 26), MUTED)
text(d, (lx+40, top+134), "812.005 + 812.003 + 812.004", font("mono", 32), INK)
text(d, (lx+40, top+184), "= 2436.012 ÷ 3 = 812.004", font("mono", 32), INK)
text(d, (lx+40, top+244), "812.004 − 812.000 = +0.004", font("mono", 36), INK)
text(d, (lx+w//2, top+324), "+0.004 ≤ 0.010", font("monob", 48), GOOD, anchor="ma")
chip(d, (lx+w//2-95, top+382), "GOOD", GOOD)
text(d, (rx+40, top+96), "S6 · 18 Apr · before it went in", font("sans", 26), MUTED)
text(d, (rx+40, top+134), "812.012 + 812.015 + 812.012", font("mono", 32), INK)
text(d, (rx+40, top+184), "= 2436.039 ÷ 3 = 812.013", font("mono", 32), INK)
text(d, (rx+40, top+244), "812.013 − 812.000 = +0.013", font("mono", 36), INK)
text(d, (rx+w//2, top+324), "+0.013 > 0.010", font("monob", 48), BAD, anchor="ma")
chip(d, (rx+w//2-150, top+382), "IT GOES BACK", BAD)
footer(d, "6 / 9")
add(im, 9.0)
# =========================================================================
# 6b. WHY NOT TALMADGE'S NUMBER (Estefana's question)
# =========================================================================
im, d = canvas()
header(d, "ESTEFANA'S QUESTION", "Why not just use Talmadge's number?")
wrap(d, "His sheet certifies the gun on a 3-pillar bench: ±(2 mm + 2 ppm). Work that at our "
"812.000 ft and set it beside the office limit.",
font("sans", 38), 90, 244, W-180, MUTED)
lx, rx, w, top = 120, 1030, 760, 400
# left — the brochure allowance
d.rounded_rectangle((lx, top, lx+w, top+70), radius=14, fill=LINE)
text(d, (lx+w//2, top+8), "TALMADGE'S SHEET", font("bold", 40), INK, anchor="ma")
panel(d, (lx, top+80, lx+w, top+440), fill=PANEL, outline=LINE, width=2)
text(d, (lx+30, top+106), "±(2 mm + 2 ppm), prism mode", font("mono", 30), INK)
text(d, (lx+30, top+156), "worked at our 812.000 ft:", font("sans", 28), MUTED)
text(d, (lx+30, top+212), "2 mm → 0.007 ft", font("mono", 30), INK)
text(d, (lx+30, top+260), "2 ppm × 812.000 → 0.002 ft", font("mono", 30), INK)
text(d, (lx+w//2, top+338), "the pair → ±0.008 ft", font("monob", 44), INK, anchor="ma")
text(d, (lx+w//2, top+400), "(each rounded to 0.001 ft)", font("sans", 22), MUTED, anchor="ma")
# right — the office limit
d.rounded_rectangle((rx, top, rx+w, top+70), radius=14, fill=ACCENT)
text(d, (rx+w//2, top+8), "OUR OFFICE LIMIT", font("bold", 40), (30,26,12), anchor="ma")
panel(d, (rx, top+80, rx+w, top+440), fill=(44,38,24), outline=ACCENT, width=2)
text(d, (rx+w//2, top+170), "±0.010 ft", font("monob", 84), ACCENT, anchor="ma")
text(d, (rx+w//2, top+300), "the number in the book", font("sans", 32), INK, anchor="ma")
# the one line — which we use and why
panel(d, (90, 866, W-90, 950), fill=GOODBG, outline=GOOD, width=2, radius=16)
text(d, (W//2, 882), "We use 0.010 — our field limit covers the whole setup on our line,",
font("bold", 32), INK, anchor="ma")
text(d, (W//2, 920), "not the gun alone on Talmadge's bench.", font("bold", 32), INK, anchor="ma")
footer(d, "7 / 9")
add(im, 11.0)
# =========================================================================
# 7. WRITE IT DOWN (good vs bad)
# =========================================================================
im, d = canvas()
header(d, "DON'T TRUST YOUR MEMORY", "Write the check down")
wrap(d, "Same page every time, in the case. Date, gun, conditions, PPM, the three sets, the mean, "
"the deviation, and the call. Initial it.",
font("sans", 38), 90, 244, W-180, MUTED)
lx, rx, w, top = 120, 1030, 760, 430
goodbad_headers(d, lx, rx, top, w)
panel(d, (lx, top+80, lx+w, top+470), fill=GOODBG, outline=GOOD, width=2)
panel(d, (rx, top+80, rx+w, top+470), fill=BADBG, outline=BAD, width=2)
good_note = ["16 Jul 26 S6 93441234","BL-1-BL-2 812.000","97F 26.40 PPM +52",
"1 812.005","2 812.003","3 812.004","mean 812.004 +0.004","GOOD - stays AIQ"]
for i,ln in enumerate(good_note):
text(d, (lx+30, top+104+i*44), ln, font("mono", 30), INK)
bad_note = ["S6 93441234","(no ppm noted)","812.005 812.037 812.003","avg 812.015 ~ok"]
for i,ln in enumerate(bad_note):
text(d, (rx+30, top+104+i*52), ln, font("mono", 32), INK)
text(d, (rx+30, top+332), "No date. No PPM.", font("sans", 30), BAD)
text(d, (rx+30, top+374), "Averaged a strike (the 812.037).", font("sans", 30), BAD)
text(d, (rx+30, top+416), "+0.015 is over 0.010 — wrong call.", font("sans", 30), BAD)
footer(d, "8 / 9")
add(im, 10.0)
# =========================================================================
# 8. THE REAL JULY RUN (worked end to end)
# =========================================================================
im, d = canvas()
header(d, "HOW IT WENT — 16 JUL", "The real run, start to finish")
jt = load_fit(os.path.join(IN, "IMG_4639.jpg"), 600, 700)
im.paste(jt, (W-90-jt.width, 250))
text(d, (W-90-jt.width, 250+jt.height+8), "Abe's page, 16 Jul.", font("sans", 26), MUTED)
x, y, lh = 90, 262, 60
lines = [
("Back from LEIS. Tripod both ends.", INK),
("97°F, 26.40 → PPM +52.", INK),
("First shot PPM was 0 — 811.962. Struck it.", BAD),
("Set PPM. Shot again:", INK),
(" Set 1 812.005", INK),
(" Set 2 812.003", INK),
(" Set 3 812.004", INK),
("Mean 812.004", ACCENT),
("812.004 − 812.000 = +0.004", ACCENT),
]
for s,c in lines:
mono = any(k in s for k in ("Set","Mean","812","→","First"))
text(d, (x, y), s, font("mono", 42) if mono else font("sans", 42), c); y += lh
chip(d, (x, y+16), "+0.004 ≤ 0.010 — GOOD, it stays", GOOD)
footer(d, "9 / 9")
add(im, 10.0)
# =========================================================================
# QUIZ --- Abe's real loaner-S5 page. Arithmetic and call cut off on purpose. ---
# To use a different page later, drop the image in as baseline_quiz_page.jpg and
# update the three sets and the worked call below.
# =========================================================================
QPATH = os.path.join(OUT, "baseline_quiz_page.jpg")
# --- the page: read it, take ten seconds
im, d = canvas()
header(d, "YOUR TURN — CALL IT BEFORE THE VIDEO DOES", "Loaner S5, 22 Jun — before it goes out")
qimg = load_fit(QPATH, 680, 770)
im.paste(qimg, (90, 244))
text(d, (90, 244+qimg.height+8), "Abe's page. Arithmetic and call cut off on purpose.",
font("sans", 24), MUTED)
rx0 = 830
wrap(d, "This is the loaner S5 (SN 30712216) from Llano — checked before it went out on a job. "
"Read the page. Take ten seconds. Then work it and call it.",
font("sans", 34), rx0, 250, W-90-rx0, MUTED)
panel(d, (rx0, 430, W-90, 902), fill=PANEL, outline=LINE)
text(d, (rx0+30, 456), "On the page:", font("bold", 30), ACCENT)
for i,s in enumerate([
"Published 812.000 · tripod both ends",
"88°F 26.55 → PPM +47",
"1 812.008",
"2 812.031 → leg in sand, reshot 812.010",
"3 812.009"]):
text(d, (rx0+30, 512+i*68), s, font("mono", 32), INK)
text(d, (rx0+30, 852), "Work the mean. Make the call.", font("bold", 36), INK)
add(im, 7.0)
# --- ten seconds
for n in range(10, 0, -1):
im, d = canvas()
header(d, "YOUR TURN — CALL IT", "Ten seconds")
tw = load_fit(QPATH, 430, 560); im.paste(tw, (90, 260))
panel(d, (600, 300, 1300, 636), outline=LINE)
for i,(lab,val) in enumerate([("Set 1","812.008"),("Set 2","812.010"),("Set 3","812.009")]):
text(d, (640, 338+i*90), lab, font("sans", 42), MUTED)
text(d, (1260, 338+i*90), val, font("mono", 52), INK, anchor="ra")
text(d, (950, 600), "Set 2 reshot after the leg moved.", font("sans", 26), MUTED, anchor="ma")
d.ellipse((1470, 380, 1710, 620), outline=ACCENT, width=8)
text(d, (1590, 392), str(n), font("bold", 150), ACCENT, anchor="ma")
add(im, 1.0, fade=False) # hard cuts → the ten-count is exactly 10.0 s
# --- work it on the screen and call it
im, d = canvas()
header(d, "THE CALL", "Loaner S5 — work it and call it", tcol=ACCENT)
panel(d, (90, 250, 900, 650), outline=LINE)
text(d, (130, 284), "22 Jun · loaner S5 · SN 30712216", font("sans", 28), MUTED)
for i,(lab,val) in enumerate([("Set 1","812.008"),("Set 2","812.010"),("Set 3","812.009")]):
text(d, (130, 342+i*80), lab, font("sans", 40), MUTED)
text(d, (760, 342+i*80), val, font("mono", 50), INK, anchor="ra")
text(d, (130, 590), "Set 2 first 812.031 — leg in the sand — struck, reshot.", font("sans", 26), MUTED)
cxr = (960 + (W-90)) // 2
panel(d, (960, 250, W-90, 650), fill=PANEL, outline=ACCENT, width=2)
text(d, (990, 300), "812.008 + 812.010 + 812.009", font("mono", 36), INK)
text(d, (990, 364), "= 2436.027 ÷ 3 = 812.009", font("mono", 36), INK)
text(d, (990, 442), "812.009 − 812.000 = +0.009", font("mono", 40), INK)
text(d, (cxr, 522), "+0.009 ≤ 0.010", font("monob", 54), GOOD, anchor="ma")
chip(d, (cxr-190, 590), "GOOD — IT GOES OUT", GOOD)
panel(d, (90, 690, W-90, 902), fill=GOODBG, outline=GOOD, width=2, radius=16)
text(d, (120, 716), "Inside 0.010 by a hair — still good.", font("bold", 34), INK)
text(d, (120, 772), "Set 2 was struck for the leg in the sand and reshot — never averaged.",
font("sans", 32), INK)
text(d, (120, 828), "Write it down: 22 Jun, S5, PPM +47, the three sets, mean 812.009, +0.009, GOOD.",
font("sans", 30), MUTED)
add(im, 13.0)
# =========================================================================
# RULE CARD (keep in the case)
# =========================================================================
im, d = canvas()
d.rectangle((0,0,W,12), fill=ACCENT)
text(d, (W//2, 56), "THE RULE CARD", font("bold", 66), INK, anchor="ma")
text(d, (W//2, 146), "Keep this in the case.", font("sans", 32), MUTED, anchor="ma")
items = [
"Any instrument back from a bench gets this before it goes out.",
"BL-1 to BL-2 = 812.000 ft, set 2016, re-observed every spring. Tripod both ends, never a pole.",
"Set up both ends: center, level, prism constant 0.0.",
"PPM off the shop gauge before the first shot. Every time.",
"Three sets, HD, feet.",
"A set > 0.005 off the other two = strike it, shoot it again. Never average.",
"Mean of three, less 812.000:",
" inside 0.010 ft → GOOD",
" over 0.010 ft → IT GOES BACK",
"Write it down: date, gun, conditions, PPM, three sets, mean, the call, initials.",
"Judge against 812.000. NOT the brochure.",
]
y = 222
maxw = W - 220
for it in items:
col = ACCENT if "GOOD" in it else (BAD if "GOES BACK" in it else INK)
if "→" in it: # the indented pass/fail lines
text(d, (230, y), it.strip(), font("monob", 34), col); y += 56
continue
fnt = font("sans", 34)
pre = "• "; prew = measure(fnt, pre)
words, line, first = it.split(), "", True
for w0 in words:
t = (line + " " + w0).strip()
if measure(fnt, t) <= (maxw - prew):
line = t
else:
text(d, (110 if first else 110+prew, y), (pre+line) if first else line, fnt, col)
y += 48; first = False; line = w0
text(d, (110 if first else 110+prew, y), (pre+line) if first else line, fnt, col)
y += 58
text(d, (W//2, H-70), "For Estefana, and whoever comes after. — Abe", font("sans", 32), MUTED, anchor="ma")
add(im, 9.0)
# =========================================================================
# ENCODE
# =========================================================================
def to_np(pil): return np.asarray(pil.convert("RGB"), dtype=np.uint8)
out_path = os.path.join(OUT, "baseline_check.mp4")
writer = imageio.get_writer(out_path, fps=FPS, codec="libx264", quality=8,
macro_block_size=8, ffmpeg_params=["-pix_fmt","yuv420p"])
XFADE = 8
prev, total = None, 0
for pil, sec, fade in SLIDES:
arr = to_np(pil)
if prev is not None and fade:
for k in range(1, XFADE+1):
a = k/(XFADE+1)
writer.append_data((prev*(1-a)+arr*a).astype(np.uint8)); total += 1
for _ in range(int(round(sec*FPS))):
writer.append_data(arr); total += 1
prev = arr
writer.close()
print(f"slides={len(SLIDES)} frames={total} dur={total/FPS:.1f}s -> {out_path}")