by adom
2020 Addressable RGB LED · 800 Kbps single-line cascade · 16 mA per channel (V1.0 sheet), 12 mA from V1.2
master
#!/usr/bin/env python3
"""WS2812B-2020 hero GLB: the coloured, side-etched, datasheet-built STEP (step2glb convert) + nonphysical footprint
reference art from the page's corrected WS2812B-2020.kicad_mod: teal dashed pad outlines 50 %, silkscreen 30 %
(pin-1 corner bracket), pad labels 50 % by signal name (DO, GND, DI, VDD from WS2812B-2020.adom-lbr.json), each
a declared viewer layer (component-reference-overlay.py + finalize.py from the LED pass). The laser etch becomes
the Adom.LaserEtch.MPN node. Real height (0.84 mm): no Z scale.
Usage: ws_hero_glb.py S2G.glb PAGE_DIR OUT.glb WORK_DIR"""
import sys, os, json, struct, subprocess, hashlib
T = '/home/adom/project/page-work/led-pass/tools'
s2g, page, out, work = sys.argv[1:5]
sha = lambda p: hashlib.sha256(open(p, 'rb').read()).hexdigest()
b = open(s2g, 'rb').read(); jn = struct.unpack_from('<I', b, 12)[0]; d = json.loads(b[20:20 + jn]); rest = b[20 + jn:]
sc = d['scenes'][d.get('scene', 0)]; kids = sc['nodes']; assert len(kids) == 1
d['nodes'][kids[0]]['name'] = 'WS2812B-2020 (STEP body)'
d['nodes'].append({'name': 'WS2812B-2020', 'children': kids}); sc['nodes'] = [len(d['nodes']) - 1]
KNOWN = {'body': ((0.8900, 0.8900, 0.8481), 0, 0.55), 'window': ((0.6383, 0.6739, 0.7106), 0, 0.2), 'terminal': ((0.5705, 0.5705, 0.5382), 0.7, 0.35),
'polarity_mark': ((0.0134, 0.0134, 0.0134), 0, 0.6), 'die_R': ((0.6035, 0.0903, 0.0624), 0, 0.45), 'die_G': ((0.0953, 0.4999, 0.2543), 0, 0.45),
'die_B': ((0.0361, 0.1300, 0.5034), 0, 0.45), 'ic_die': ((0.1005, 0.1065, 0.1193), 0, 0.4), 'laser_etch': ((0.012, 0.012, 0.012), 0, 0.6)}
for m in d['materials']:
c = m['pbrMetallicRoughness']['baseColorFactor'][:3]
role = min(KNOWN, key=lambda r: max(abs(a - x) for a, x in zip(c, KNOWN[r][0]))); assert max(abs(a - x) for a, x in zip(c, KNOWN[role][0])) < 5e-4
_, mt, rg = KNOWN[role]; m['name'] = role; m['pbrMetallicRoughness'].update(metallicFactor=mt, roughnessFactor=rg)
if role == 'window':
m['alphaMode'] = 'BLEND'; m.setdefault('extensions', {})['KHR_materials_clearcoat'] = {'clearcoatFactor': 1.0, 'clearcoatRoughnessFactor': 0.08}
if 'KHR_materials_clearcoat' not in d.setdefault('extensionsUsed', []): d['extensionsUsed'].append('KHR_materials_clearcoat')
if role == 'laser_etch': m['doubleSided'] = True
j = json.dumps(d, separators=(',', ':')).encode(); j += b' ' * ((-len(j)) % 4)
base = os.path.join(work, 'WS2812B-2020.hero-base.glb')
open(base, 'wb').write(struct.pack('<III', 0x46546C67, 2, 20 + len(j) + len(rest)) + struct.pack('<II', len(j), 0x4E4F534A) + j + rest)
lbrp = os.path.join(page, 'WS2812B-2020.adom-lbr.json'); lbr = json.load(open(lbrp))
pins = [dict(p, electrical_type='passive') for p in lbr['symbol']['pins']]
os.makedirs(os.path.join(page, 'provenance'), exist_ok=True)
pm = os.path.join(page, 'provenance', 'ws2812b-2020-hero.pinmap.json')
json.dump({'schema': 'adom/hero-pinmap@1', 'mpn': 'WS2812B-2020', 'derived_from': {'file': 'WS2812B-2020.adom-lbr.json', 'sha256': sha(lbrp)},
'note': 'Pin names and pin-to-pad connects copied from the corrected adom-lbr part (datasheet p.2: 1 DO, 2 GND, 3 DI, 4 VDD); electrical_type passive for the hero only so the overlay labels pads by signal name.',
'symbol': {'pins': pins}, 'connect': lbr['connect']}, open(pm, 'w'), indent=1)
ov = os.path.join(work, 'ws2812b-2020-hero.overlay.glb')
r = subprocess.run([sys.executable, f'{T}/component-reference-overlay.py', '--glb', base, '--footprint', os.path.join(page, 'WS2812B-2020.kicad_mod'),
'--library', pm, '--font', f'{T}/JetBrainsMono-Regular.ttf', '--out', ov, '--label-height', '0.25'], capture_output=True, text=True)
assert r.returncode == 0, r.stderr
note = ('WS2812B-2020 addressable RGB pixel, 2.20 x 2.00 x 0.84 mm (datasheet p.1-2). AI-generated model built from the datasheet: '
'white body, milky window, R/G/B die patches (lambda_d 625/520/470 nm, illustrative placement), virtual MPN etch on the -Y side.')
spec = {'component_label': 'WS2812B-2020 (coloured model)', 'component_tooltip': note,
'label_tooltip': 'Pad labels (signal names: 1 DO, 2 GND, 3 DI, 4 VDD)\nreference art, not part of the component',
'tooltips': {'WS2812B-2020 (STEP body)': note},
'etch_rgb': [0.012, 0.012, 0.012],
'etch_extras': {'adomRole': 'laser_etch', 'virtualMark': True, 'text': 'WS2812B-2020', 'face': 'side (-Y, white body band)',
'adomLayer': {'key': 'laser_etch', 'label': 'Laser etch (MPN)', 'group': 'Component'},
'tooltip': 'Virtual laser-etched MPN (WS2812B-2020)\nidentification aid, not the manufacturer marking'},
'asset_extras': {'adomHero': {'mpn': 'WS2812B-2020', 'kind': 'component + footprint reference art', 'board_use': False,
'model': 'AI-generated from the WS2812B-2020 datasheet V1.0 (no redistributable vendor or KiCad model exists)'}}}
sp = os.path.join(work, 'ws-hero.finalize.json'); json.dump(spec, open(sp, 'w'))
r = subprocess.run([sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'finalize.py'), ov, out, sp], capture_output=True, text=True); assert r.returncode == 0, r.stderr
print(r.stdout.strip())