54 lines
2.4 KiB
Python
54 lines
2.4 KiB
Python
"""Render a 2D overview of the generated STL plate using Pillow.
|
||
|
||
The preview is only a placement guide; the STL is the printing artifact.
|
||
"""
|
||
from pathlib import Path
|
||
import json, struct
|
||
from PIL import Image, ImageDraw, ImageFont, ImageChops, ImageFilter
|
||
|
||
root=Path(__file__).resolve().parent.parent
|
||
out=root/'models'/'pcd50_print_plate'
|
||
source=root/'models'/'pcd50_open_pillar_v1'
|
||
info=json.loads((out/'布局检查.json').read_text(encoding='utf-8'))
|
||
|
||
canvas=Image.new('RGB',(1220,1060),'#f7f9fc')
|
||
d=ImageDraw.Draw(canvas)
|
||
font_path='C:/Windows/Fonts/msyh.ttc'
|
||
title=ImageFont.truetype(font_path,42)
|
||
font=ImageFont.truetype(font_path,25)
|
||
small=ImageFont.truetype(font_path,21)
|
||
d.text((70,38),'一盘打印:支架 + 4 个 0.5 mm 垫片',font=title,fill='#1a2942')
|
||
d.text((70,104),'俯视排版 · 模型保持原尺寸 · 单位:mm',font=font,fill='#52627b')
|
||
|
||
scale=10.0; ox=250; oy=900
|
||
def xy(x,y): return (round(ox+x*scale),round(oy-y*scale))
|
||
def triangle_vertices(path):
|
||
data=path.read_bytes()
|
||
count=struct.unpack_from('<I',data,80)[0]
|
||
if len(data)!=84+50*count:raise ValueError('Invalid binary STL')
|
||
return [((t[3],t[4],t[5]),(t[6],t[7],t[8]),(t[9],t[10],t[11]))
|
||
for t in struct.iter_unpack('<12fH',data[84:])]
|
||
|
||
def show_part(path, target_bounds, fill):
|
||
tris=triangle_vertices(path)
|
||
source_x=min(v[0] for tri in tris for v in tri)
|
||
source_y=min(v[1] for tri in tris for v in tri)
|
||
dx,dy=target_bounds[0]-source_x,target_bounds[2]-source_y
|
||
mask=Image.new('L',canvas.size,0);md=ImageDraw.Draw(mask)
|
||
for tri in tris:
|
||
md.polygon([xy(v[0]+dx,v[1]+dy) for v in tri],fill=255)
|
||
ink=Image.new('RGB',canvas.size,fill);canvas.paste(ink,(0,0),mask)
|
||
edge=ImageChops.subtract(mask,mask.filter(ImageFilter.MinFilter(5)))
|
||
canvas.paste(Image.new('RGB',canvas.size,'#20314b'),(0,0),edge)
|
||
|
||
# Plate bounds reflect model footprint, not an assumed printer bed.
|
||
d.rounded_rectangle([215,oy-725,ox+755,oy+35],radius=12,fill='#ffffff',outline='#c3cfdf',width=3)
|
||
for ob in info['main']['objects']:
|
||
part='EncoderBracket.stl' if ob['name']=='Bracket' else 'Shim05.stl'
|
||
show_part(source/part,ob['bounds_mm'],'#3c6fba' if ob['name']=='Bracket' else '#e3a53e')
|
||
d=ImageDraw.Draw(canvas)
|
||
d.text((270,185),'支架 × 1',font=font,fill='#214b87')
|
||
d.text((270,922),'0.5 mm 垫片 × 4',font=font,fill='#8a5600')
|
||
d.text((70,1000),'模型约 71.6 × 69.0 mm,件间净距至少 8.0 mm',font=small,fill='#52627b')
|
||
canvas.save(out/'打印排版预览.png')
|