master
John Lauer Correct resistor hero binding to LED; add gentle one-shot preview and descriptive component identity 81dcafb 1mo ago
import pathlib,json,struct,math,hashlib
B=pathlib.Path(__file__).resolve().parent
for slug in ['ncd0402g1','ncd0402b1']:
 p=B/slug/(slug+'-mpn-led-on.glb');raw=p.read_bytes();n=struct.unpack_from('<I',raw,12)[0];j=json.loads(raw[20:20+n]);bl=bytearray(raw[28+n:]);bl=bl[:j['buffers'][0]['byteLength']]
 def acc(vals,typ):
  nonlocal_dummy=0
  while len(bl)%4:bl.append(0)
  off=len(bl);flat=[x for v in vals for x in (v if isinstance(v,list) else [v])];bl.extend(struct.pack('<'+'f'*len(flat),*flat));vi=len(j['bufferViews']);j['bufferViews'].append({'buffer':0,'byteOffset':off,'byteLength':len(flat)*4});ai=len(j['accessors']);a={'bufferView':vi,'componentType':5126,'count':len(vals),'type':typ}
  if typ=='SCALAR':a.update(min=[min(vals)],max=[max(vals)])
  j['accessors'].append(a);return ai
 times=[i/20 for i in range(201)];ti=acc(times,'SCALAR');a={'name':'Gentle LED preview - one play then lit and still','samplers':[],'channels':[]}
 def chan(vals,typ,target):
  si=len(a['samplers']);a['samplers'].append({'input':ti,'output':acc(vals,typ),'interpolation':'LINEAR'});a['channels'].append({'sampler':si,'target':target})
 def pointer(vals,typ,path):chan(vals,typ,{'path':'pointer','extensions':{'KHR_animation_pointer':{'pointer':path}}})
 def smooth(t):t=max(0,min(1,t));return t*t*(3-2*t)
 # Lit at initial/static frame, fade down once and gently back up, then hold.
 level=[1-smooth((t-1)/2.5) if t<3.5 else smooth((t-4)/3) for t in times]
 root=len(j['nodes']);roots=j['scenes'][j.get('scene',0)]['nodes'];j['nodes'].append({'name':'Adom.Hero.Presentation','children':roots,'extras':{'adomRole':'hero-presentation','nonphysical':True}});j['scenes'][j.get('scene',0)]['nodes']=[root]
 angles=[math.radians(162+36*smooth(t/8)) for t in times]
 chan([[0,0,math.sin(v/2),math.cos(v/2)] for v in angles],'VEC4',{'node':root,'path':'rotation'})
 for i,m in enumerate(j['materials']):
  if m.get('name','').startswith('Adom.LED.Emissive'):
   rgb=m['emissiveFactor'];pointer([[x*f for x in rgb] for f in level],'VEC3',f'/materials/{i}/emissiveFactor')
   pointer([[(.12*(1-f)+x*f) for x in rgb]+[1] for f in level],'VEC4',f'/materials/{i}/pbrMetallicRoughness/baseColorFactor')
 light=j['extensions']['KHR_lights_punctual']['lights'][0];pointer([light['intensity']*f for f in level],'SCALAR','/extensions/KHR_lights_punctual/lights/0/intensity')
 j['animations']=[a];j.setdefault('extensionsUsed',[]).append('KHR_animation_pointer');j['buffers'][0]['byteLength']=len(bl)
 j['asset']['extras']['heroPreview']={'seconds':10,'repeat':1,'final':'LED on, rotation stopped','source':p.name,'sourceSha256':hashlib.sha256(raw).hexdigest(),'notice':'Presentation-only animation; use static LED variants in boards. Generic KiCad LED body, not manufacturer-qualified geometry.'}
 js=json.dumps(j,separators=(',',':')).encode();js+=b' '*(-len(js)%4);bl+=b'\0'*(-len(bl)%4);out=struct.pack('<III',0x46546c67,2,28+len(js)+len(bl))+struct.pack('<II',len(js),0x4e4f534a)+js+struct.pack('<II',len(bl),0x004e4942)+bl
 dest=B/slug/(slug+'-hero.glb');dest.write_bytes(out);print(dest,len(out))