import json,struct,numpy as np
b=open('kit.glb','rb').read();n=struct.unpack_from('<I',b,12)[0];g=json.loads(b[20:20+n]);binary=b[28+n:];s=json.load(open('materials.json'))['materials']
def acc(i):
 a=g['accessors'][i];v=g['bufferViews'][a['bufferView']];dt={5126:'<f4',5125:'<u4',5123:'<u2',5121:'u1'}[a['componentType']];c={'SCALAR':1,'VEC2':2,'VEC3':3,'VEC4':4}[a['type']];off=v.get('byteOffset',0)+a.get('byteOffset',0);item=np.dtype(dt).itemsize
 return np.ndarray((a['count'],c),dtype=dt,buffer=binary,offset=off,strides=(v.get('byteStride',item*c),item))
maximum=0
for m in g['meshes']:
 for p in m['primitives']:
  pos=acc(p['attributes']['POSITION']);uv=acc(p['attributes']['TEXCOORD_0']);ids=acc(p['indices']).flatten().reshape(-1,3);tile=s[g['materials'][p['material']]['name']]['tile_size_metres']
  for edge in [(0,1),(1,2),(2,0)]:
   a,c=ids[:,edge[0]],ids[:,edge[1]];expected=np.linalg.norm(pos[a]-pos[c],axis=1);actual=np.linalg.norm(uv[a]-uv[c],axis=1)*tile;error=np.max(abs(expected-actual));maximum=max(maximum,float(error))
assert maximum<2e-5,maximum
json.dump({'metric_uv_pass':True,'maximum_edge_length_error_metres':maximum},open('uv_validation.json','w'),indent=2);print(maximum)
