master
John Lauer Cache optional fitted MPN lettering and reviewed variant provenance b627ed3 1mo ago
import cadquery as cq

def load(p):
 try:
  a=cq.Assembly.load(str(p))
  if not a.toCompound().Vertices():raise ValueError('Empty assembly mapping')
  return a
 except (ValueError,KeyError):return cq.Assembly(cq.Compound.makeCompound(cq.importers.importStep(str(p)).vals()),name='OriginalBody',color=cq.Color(.25,.25,.25))
def fit(src,out,fallback_text=None):
 original=load(src);shape=original.toCompound();pb=shape.BoundingBox();marked=load(out);text=[]
 for _,n in marked.traverse():
  if n.obj:
   s=n.toCompound();b=s.BoundingBox()
   if b.zmin>=pb.zmax+.0001:text.extend(s.Solids())
 if not text and not fallback_text:raise ValueError('Cannot isolate generated text above original body')
 text=cq.Workplane('XY').text(fallback_text,1,.01,font='DejaVu Sans',combine=False).val() if fallback_text else cq.Compound.makeCompound(text);tb=text.BoundingBox();text=text.translate((-(tb.xmin+tb.xmax)/2,-(tb.ymin+tb.ymax)/2,-tb.zmin))
 faces=[]
 for f in shape.Faces():
  if f.geomType()=='PLANE' and f.normalAt().z>.99 and f.Area()>.005:faces.append(f)
 if not faces:raise ValueError('No planar upward surface')
 top=max(f.Center().z for f in faces);faces=[f for f in faces if f.Center().z>=top-min(.15,.25*pb.zlen)]
 best=None
 for f in faces:
  b=f.BoundingBox()
  if min(b.xlen,b.ylen)<.01:continue
  for angle in [0,90]:
   t=text.rotate((0,0,0),(0,0,1),angle);bb=t.BoundingBox();ratio=min(.86*b.xlen/bb.xlen,.7*b.ylen/bb.ylen)
   for factor in [1,.8,.6,.4,.25,.15,.08,.04]:
    scale=ratio*factor;w=bb.xlen*scale;h=bb.ylen*scale
    found=False
    for ox,oy in [(x,y) for x in [0,-.22,.22,-.38,.38,-.46,.46] for y in [0,-.22,.22,-.38,.38,-.46,.46]]:
     cx=(b.xmin+b.xmax)/2+ox*b.xlen;cy=(b.ymin+b.ymax)/2+oy*b.ylen;z=f.Center().z
     if all(f.distance(cq.Vertex.makeVertex(cx+u*w/2,cy+v*h/2,z))<1e-5 for u in [-1,-.5,0,.5,1] for v in [-1,0,1]):
      if best is None or scale>best[0]:best=(scale,t,cx,cy,z,angle,w,h)
      found=True;break
    if found:break
 if best is None:raise ValueError('No safe text rectangle on upward surface')
 scale,t,cx,cy,z,angle,w,h=best;t=t.rotate((0,0,0),(0,0,1),180);angle=(angle+180)%360;bb=t.BoundingBox();scaled=t.scale(scale);sb=scaled.BoundingBox()
 # Keep true STEP text geometry thin relative to even the smallest package.
 mat=cq.Matrix([[1,0,0,0],[0,1,0,0],[0,0,.001/sb.zlen,0],[0,0,0,1]])
 scaled=scaled.transformGeometry(mat).translate((cx,cy,z+.0005))
 append_preserving_document(src,out,scaled)
 cq.Assembly(scaled,name='Adom.LaserEtch.MPN',color=cq.Color(.82,.84,.83)).export(str(out.with_name(out.stem+'-overlay.step')))
 return {'fittedTextBoundsMm':[w,h,.001],'textCenterMm':[cx,cy,z+.0005],'textRotationDegrees':angle,'generatedTextScale':scale,'fitRule':'Largest sampled rectangle on upper planar body face (including body below raised terminals); 86% length and 70% width maximum, both orientations compared; 1um identification geometry; sampled face containment excludes holes','plainGeometryPreserved':'Original assembly retained; only separate text group added'}

def append_preserving_document(src,out,mark):
 from OCP.TDocStd import TDocStd_Document
 from OCP.TCollection import TCollection_ExtendedString
 from OCP.STEPCAFControl import STEPCAFControl_Reader,STEPCAFControl_Writer
 from OCP.XCAFDoc import XCAFDoc_DocumentTool,XCAFDoc_ColorType
 from OCP.TDataStd import TDataStd_Name
 from OCP.Quantity import Quantity_Color,Quantity_TOC_RGB
 from OCP.STEPControl import STEPControl_AsIs
 doc=TDocStd_Document(TCollection_ExtendedString('BinXCAF'));reader=STEPCAFControl_Reader();reader.SetColorMode(True);reader.SetNameMode(True);reader.ReadFile(str(src));assert reader.Transfer(doc)
 shapes=XCAFDoc_DocumentTool.ShapeTool_s(doc.Main());colors=XCAFDoc_DocumentTool.ColorTool_s(doc.Main());label=shapes.AddShape(mark.wrapped,False);TDataStd_Name.Set_s(label,TCollection_ExtendedString('Adom.LaserEtch.MPN'));colors.SetColor(label,Quantity_Color(.82,.84,.83,Quantity_TOC_RGB),XCAFDoc_ColorType.XCAFDoc_ColorSurf)
 writer=STEPCAFControl_Writer();writer.SetColorMode(True);writer.SetNameMode(True);assert writer.Transfer(doc,STEPControl_AsIs);writer.Write(str(out))