gokul/macro-six-usbc
USB-powered ATmega32U4 controller board with protected/switched 5 V power, six tactile keys, rotary encoder, reset/boot/ISP interfaces, power LED, and six daisy-chained SK6812 RGB LEDs driven through MOSFETs.
- Version
- 0.1.6
- License
- MIT
- Stars
- 0
enclosure/render-preview.py
"""Orthographic, depth-buffered CAD previews. Z is up; units are mm."""
from pathlib import Path
import math
import numpy as np
import trimesh
from PIL import Image,ImageDraw,ImageFont
OUT=Path(__file__).resolve().parent/"exports"
def render(stem,output,elev,azim,title=None,subtitle=None):
scene=trimesh.load(OUT/(stem+".glb"),force="scene")
W,H=1920,1280
image=np.full((H,W,3),[237,240,243],dtype=np.uint8)
depth=np.full((H,W),np.inf,dtype=np.float32)
a,e=math.radians(azim),math.radians(elev)
camera=np.array([math.cos(e)*math.cos(a),math.cos(e)*math.sin(a),math.sin(e)])
forward=-camera;right=np.cross(forward,[0,0,1]);right/=np.linalg.norm(right)
up=np.cross(right,forward);matrix=np.vstack([right,up,forward]).T
all_triangles=[];all_colors=[]
light=np.array([-.25,-.45,.86]);light/=np.linalg.norm(light)
for node in scene.graph.nodes_geometry:
transform,key=scene.graph[node];m=scene.geometry[key].copy();m.apply_transform(transform)
if m.visual.kind == 'texture':
color=np.asarray(m.visual.material.baseColorFactor[:3])/255
else:
color=np.asarray(m.visual.main_color[:3])/255
shade=.72+.28*np.clip(m.face_normals@light,-.8,1)
colors=np.clip(np.asarray(color)[None,:]*shade[:,None]*255,0,255).astype(np.uint8)
all_triangles.append(m.triangles@matrix);all_colors.append(colors)
tris=np.vstack(all_triangles);colors=np.vstack(all_colors)
lo=tris[:,:,:2].min(axis=(0,1));hi=tris[:,:,:2].max(axis=(0,1));span=hi-lo
scale=min((W-180)/span[0],(H-280)/span[1])
tris[:,:,0]=(tris[:,:,0]-lo[0])*scale+(W-span[0]*scale)/2
tris[:,:,1]=H-145-(tris[:,:,1]-lo[1])*scale
for tri,color in zip(tris,colors):
xmin=max(0,int(np.floor(tri[:,0].min())));xmax=min(W-1,int(np.ceil(tri[:,0].max())))
ymin=max(0,int(np.floor(tri[:,1].min())));ymax=min(H-1,int(np.ceil(tri[:,1].max())))
if xmin>xmax or ymin>ymax:continue
(x0,y0,z0),(x1,y1,z1),(x2,y2,z2)=tri
denominator=(y1-y2)*(x0-x2)+(x2-x1)*(y0-y2)
if abs(denominator)<1e-8:continue
ys,xs=np.ogrid[ymin:ymax+1,xmin:xmax+1]
w0=((y1-y2)*(xs+.5-x2)+(x2-x1)*(ys+.5-y2))/denominator
w1=((y2-y0)*(xs+.5-x2)+(x0-x2)*(ys+.5-y2))/denominator
w2=1-w0-w1
z=w0*z0+w1*z1+w2*z2
tile=depth[ymin:ymax+1,xmin:xmax+1]
mask=(w0>=-1e-6)&(w1>=-1e-6)&(w2>=-1e-6)&(z<tile)
tile[mask]=z[mask];image[ymin:ymax+1,xmin:xmax+1][mask]=color
out=Image.fromarray(image);d=ImageDraw.Draw(out)
fonts=Path('/System/Library/Fonts/Supplemental')
titlefont=ImageFont.truetype(str(fonts/'Arial Bold.ttf'),37)
font=ImageFont.truetype(str(fonts/'Arial.ttf'),23)
title=title or ("MACRO SIX / EXPLODED CAD" if "exploded" in stem else "MACRO SIX / ENCLOSURE CAD")
d.text((85,52),title,font=titlefont,fill='#253544')
d.text((85,H-70),subtitle or "116.4 × 74.4 × 20 mm housing • Keycaps and knob are fit references",font=font,fill='#526575')
out.save(OUT/output)
print(output,flush=True)
if __name__=='__main__':
render("enclosure-preview","enclosure-preview.png",35,-63)
render("enclosure-preview","enclosure-rear.png",33,120)
render("enclosure-exploded","enclosure-exploded.png",25,-63)