abse/museair-usb

USB-C–powered ESP32-C3 controller board with AP63203 buck conversion, AP2112K 3.3V regulation, I²C SEN62 sensor interface, status LED, buttons, UART/I²C headers, and extensive decoupling.

Version
0.5.7
License
unset
Stars
0

scripts/sync-assembly-pcb.py

# Superseded for assembly appearance: use package-native-assembly-pcb.py with
# an actual native CadViewer capture. This CPU exporter differs in materials.
"""Pose a fresh main-index GLB in the assembly, preserving original CAD.
Input is produced by export-current-glb.mjs. Only the separately assembled
sensor is excluded; PCB artwork, component geometry and materials are kept.
"""
from pathlib import Path
import sys,json,hashlib,numpy as np,trimesh
root=Path(__file__).resolve().parent.parent
source=Path(sys.argv[1]);scene=trimesh.load(source,force='scene')
for node in list(scene.graph.nodes_geometry):
    if node.startswith('U3_'):scene.graph.transforms.remove_node(node)
used={scene.graph[node][1] for node in scene.graph.nodes_geometry}
for geom in list(scene.geometry):
    if geom not in used:scene.geometry.pop(geom)
# Exporter has X mirror and Y/Z exchange; same accepted PCB mounting pose.
restore=np.array([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]],float)
pose=np.array([[0,-1,0,2.5],[1,0,0,0],[0,0,1,37.8],[0,0,0,1]],float)
scene.apply_transform(pose@restore)
for mesh in scene.geometry.values():
    triangles=mesh.triangles.copy();mesh.merge_vertices(merge_tex=False,merge_norm=False)
    assert np.allclose(triangles,mesh.triangles,atol=1e-7,rtol=0)
scene.export(root/'mechanical/horizontal/models/pcb.glb')
ref=scene.copy();ref.apply_transform(restore@np.linalg.inv(pose));ref.export(root/'mechanical/horizontal/reference/pcb-imported-display.glb')
components=sorted({n.split('_')[0] for n in scene.graph.nodes_geometry if not n.startswith('Box0')})
circuit=json.loads((root/'dist/index/circuit.json').read_text());sources={x['source_component_id']:x['name'] for x in circuit if x['type']=='source_component'}
expected=sorted({sources[x['source_component_id']] for x in circuit if x['type']=='cad_component' and sources[x['source_component_id']]!='U3'})
assert components==expected,(components,expected)
textures=[list(m.visual.material.baseColorTexture.size) for m in scene.geometry.values() if getattr(getattr(m.visual,'material',None),'baseColorTexture',None) is not None]
report={'source':'dist/index/circuit.json','circuit_sha256':hashlib.sha256((root/'dist/index/circuit.json').read_bytes()).hexdigest(),'component_models':components,'component_count':len(components),'all_index_cad_components_preserved_except_separate_sensor':True,'triangles_preserved':True,'texture_dimensions':textures,'texture_downsampling':False,'pcb_pose_preserved':True}
(root/'validation/assembly-pcb-model-sync.json').write_text(json.dumps(report,indent=2)+'\n');print('PASS: main-index CAD synchronized:',len(components),'component models;',textures)