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)