# Measuring a frame: the shares of the surfaces, the borders of the panels, the heights of the # strips, the baselines of the text. The tool exists for the sake of the norm "work by measurement # and not by eye" (FRONTEND_PLAN.md §5): every session that touches scale and gaps is bound to bring # numbers rather than an impression. # # .tooling/py/bin/python scripts/measure.py references/fleet.png .shots/showcase.png # # Both frames are brought to CSS pixels by their own scale (the --scale argument, 2 by default — # both the reference and our shot were taken at deviceScaleFactor 2). # # Pillow is pinned in scripts/requirements.txt — the procedure of installing it is there too. The # system python on the stand comes without it, hence the venv in .tooling/py (it does not travel # into git). import sys from collections import Counter from PIL import Image def surfaces(image, top=8): counts = Counter(image.getdata()) total = sum(counts.values()) return [ ('#%02x%02x%02x' % color, round(100 * n / total, 2)) for color, n in counts.most_common(top) ] def runs(values, minimum=2): """Runs of one and the same value in a row: (value, start, length).""" out = [] start = 0 for index in range(1, len(values) + 1): if index == len(values) or values[index] != values[start]: if index - start >= minimum: out.append((values[start], start, index - start)) start = index return out def column_profile(image, y): """The colours along a horizontal cut — this is how panel borders and gaps become visible.""" return [image.getpixel((x, y)) for x in range(image.width)] def row_profile(image, x): return [image.getpixel((x, y)) for y in range(image.height)] def report(path, scale): image = Image.open(path).convert('RGB') print(f'\n=== {path} — {image.width}x{image.height} phys, {image.width // scale}x{image.height // scale} CSS ===') print('surfaces (share of the frame):') for color, share in surfaces(image): print(f' {color} {share:5.2f}%') middle = image.height // 2 # The probe is taken at the centre of the top strip and not in a corner: on the reference the # corners of the macOS window are rounded, and in the corner lies the black ground of the # desktop, not the ground of the shell. shell = image.getpixel((image.width // 2, 2 * scale)) print('\nground of the shell (top strip): #%02x%02x%02x' % shell) # A horizontal cut through the middle: the runs of the shell's ground = the gaps between panels. strip = column_profile(image, middle) gaps = [(start, length) for color, start, length in runs(strip) if color == shell] print('gaps of the ground horizontally (CSS: start, width):') for start, length in gaps: print(f' x={start / scale:7.1f} w={length / scale:5.1f}') # A vertical cut down the left edge of the panel: the top strip and the status strip. x = gaps[0][0] + gaps[0][1] + 4 if gaps else 20 column = row_profile(image, x) bands = [(start, length) for color, start, length in runs(column) if color == shell] print(f'bands of the ground vertically at x={x / scale:.1f} (CSS: start, height):') for start, length in bands: print(f' y={start / scale:7.1f} h={length / scale:5.1f}') if __name__ == '__main__': args = [a for a in sys.argv[1:] if not a.startswith('--')] scale = 2 for a in sys.argv[1:]: if a.startswith('--scale='): scale = int(a.split('=')[1]) for path in args: report(path, scale)