Files
Sanctification/artifacts/layers/build.py

69 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""Rebuild shared templates, or check their decoded-pixel repeatability."""
from pathlib import Path
import argparse, json, subprocess, tempfile
import numpy as np
from PIL import Image
from library import ROOT, digest, load_tier
NAMES=['frame-normal','frame-textless','backing-normal','backing-borderless','overlay-normal']
def rebuild(tier,version,refresh=False):
directory=ROOT/tier/version
layout=json.loads((directory/'layout.json').read_text())
if not refresh: load_tier(tier,version)
with tempfile.TemporaryDirectory(prefix='sanctification-tier-') as tmp:
temporary=Path(tmp)
for name in NAMES[:-1]:
target=temporary/f'{name}-2000.png'
if version=='v1':
Image.open(directory/'exports'/target.name).save(target)
else:
source=directory/'source'/f'{name}.svg'
subprocess.run(['inkscape',str(source),'--export-type=png',f'--export-filename={target}','--export-width=2000','--export-height=2800'],check=True,stdout=subprocess.DEVNULL)
if version=='v1':
Image.open(directory/'exports/overlay-normal-2000.png').save(temporary/'overlay-normal-2000.png')
else:
back=Image.open(temporary/'backing-normal-2000.png').convert('RGBA')
Image.alpha_composite(back,Image.open(temporary/'frame-normal-2000.png').convert('RGBA')).save(temporary/'overlay-normal-2000.png')
for name in NAMES:
master=Image.open(temporary/f'{name}-2000.png').convert('RGBA')
for width in [1000,500]:
master.resize((width,width*7//5),Image.Resampling.LANCZOS).save(temporary/f'{name}-{width}.png')
for width in [2000,1000,500]:
p=temporary/f'{name}-{width}.png'
if refresh: (directory/'exports'/p.name).write_bytes(p.read_bytes())
else:
expected=Image.open(directory/'exports'/p.name).convert('RGBA')
assert np.array_equal(np.asarray(expected),np.asarray(Image.open(p).convert('RGBA'))),f'Non-repeatable {tier}/{version}/{p.name}'
assets={name:f'exports/{name}-2000.png' for name in NAMES}
if refresh:
files={str(p.relative_to(directory)):{'sha256':digest(p)} for p in sorted(directory.rglob('*')) if p.is_file() and p.name!='manifest.json'}
(directory/'manifest.json').write_text(json.dumps({'schemaVersion':1,'tier':tier,'version':version,'canvas':[2000,2800],'reviewStatus':layout['reviewStatus'],'assets':assets,'files':files},indent=2)+'\n')
checks=[]
for width in [2000,1000,500]:
for name in ['frame-normal','frame-textless','overlay-normal']:
a=np.asarray(Image.open(directory/'exports'/f'{name}-{width}.png').convert('RGBA'))[:,:,3]
assert all(a[y,x]==255 for x,y in [(0,0),(width-1,0),(0,len(a)-1),(width-1,len(a)-1)])
checks.append({'width':width,'opaqueCorners':True})
a=np.asarray(Image.open(directory/'exports/overlay-normal-2000.png').convert('RGBA'))[:,:,3]
for x,y,w,h in layout['normalBacking']:
assert np.all(a[y+10:y+h-10,x+10:x+w-10]==255),f'Panel holes in {tier}'
assert not np.any(a[430:2100,160:1840]),f'Art window obstruction in {tier}'
if layout['normalRelationship']=='floating':
band=layout['perimeterWidth']; x=layout['normalBacking'][0][0]
assert not np.any(a[220:280,band+3:x-3]),f'Lost intentional reveal in {tier}'
result={'tier':tier,'version':version,'status':'passed','repeatability':'decoded pixels checked' if not refresh else 'initial exports created','checks':checks,'geometry':{'opaquePanels':True,'clearFocalWindow':True,'relationship':layout['normalRelationship']}}
print(json.dumps(result))
return result
if __name__=='__main__':
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--refresh',action='store_true',help='Write exports/manifests for an explicitly revised template; use a new version directory after approval.')
parser.add_argument('--tier',choices=['common','uncommon','rare','extraordinary'])
parser.add_argument('--version',default='v2')
args=parser.parse_args()
results=[rebuild(tier,args.version,args.refresh) for tier in ([args.tier] if args.tier else ['common','uncommon','rare','extraordinary'])]
if not args.refresh: (ROOT/'repeatability.json').write_text(json.dumps({'status':'passed','templates':results},indent=2)+'\n')
if args.refresh: (ROOT/'validation.json').write_text(json.dumps({'status':'passed','templates':results,'visualReview':'pending-user-review','movingLight':'not performed'},indent=2)+'\n')