app
[DEPRECATED] Chip Fetcher → adom-chip-fetcher
Public Made by Adomby adom
DEPRECATED — use adom/adom-chip-fetcher. No longer maintained.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
#!/usr/bin/env python3
"""Emboss a chip's part number onto the TOP face of its 3D STEP model — real
BRep geometry, so it renders correctly in the isometric thumbnail AND travels
with the STEP into KiCad and every downstream tool.
We don't guess where "up" is — chip-fetcher already records the chip's chosen
up-axis (y or z). The top face is the max-coordinate plane on that axis; the
text lies flat on it, fitted to the face's bounding box, raised as a shallow
relief (laser-mark style).
occt-name-on-chip.py <in.step> <up_axis: y|z> <name> <out.step> [line2]
`line2` is optional — e.g. a variant/extended suffix — embossed as a smaller
second line below the main name (variants are a first-class future option).
"""
import sys, glob
from OCP.STEPControl import STEPControl_Reader, STEPControl_Writer, STEPControl_StepModelType
from OCP.IFSelect import IFSelect_RetDone
from OCP.Bnd import Bnd_Box
from OCP.BRepBndLib import BRepBndLib
from OCP.StdPrs import StdPrs_BRepFont, StdPrs_BRepTextBuilder
from OCP.NCollection import NCollection_Utf8String
from OCP.BRepPrimAPI import BRepPrimAPI_MakePrism
from OCP.BRepBuilderAPI import BRepBuilderAPI_Transform
from OCP.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCP.gp import gp_Vec, gp_Trsf, gp_Ax1, gp_Pnt, gp_Dir
from OCP.TopoDS import TopoDS_Compound
from OCP.BRep import BRep_Builder
FONT = (glob.glob('/usr/share/fonts/**/DejaVuSansMono-Bold.ttf', recursive=True)
or glob.glob('/usr/share/fonts/**/DejaVuSans-Bold.ttf', recursive=True))[0]
def read_step(path):
r = STEPControl_Reader()
if r.ReadFile(path) != IFSelect_RetDone:
raise SystemExit(f"ERROR: cannot read {path}")
r.TransferRoots()
return r.OneShape()
def bbox(shape):
b = Bnd_Box()
BRepBndLib.Add_s(shape, b)
return b.Get() # xmin,ymin,zmin,xmax,ymax,zmax
def text_shape(s, size):
"""Flat text in the XY plane, baseline at origin, growing +X. Returns
(shape, width, height)."""
font = StdPrs_BRepFont(NCollection_Utf8String(FONT), float(size))
sh = StdPrs_BRepTextBuilder().Perform(font, NCollection_Utf8String(s))
xmin, ymin, zmin, xmax, ymax, zmax = bbox(sh)
return sh, (xmax - xmin), (ymax - ymin), xmin, ymin
def fit_size(text, avail_w, avail_h):
"""Pick a font size so `text` fits avail_w × avail_h."""
ref = 10.0
_, w0, h0, _, _ = text_shape(text, ref)
if w0 <= 0 or h0 <= 0:
return ref
return max(0.2, min(ref * avail_w / w0, ref * avail_h / h0))
def translate(shape, dx, dy, dz):
t = gp_Trsf(); t.SetTranslation(gp_Vec(dx, dy, dz))
return BRepBuilderAPI_Transform(shape, t, True).Shape()
def rotate_x(shape, deg):
t = gp_Trsf(); t.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), deg * 3.141592653589793 / 180.0)
return BRepBuilderAPI_Transform(shape, t, True).Shape()
def rotate_about(shape, px, py, pz, dx, dy, dz, deg):
t = gp_Trsf(); t.SetRotation(gp_Ax1(gp_Pnt(px, py, pz), gp_Dir(dx, dy, dz)), deg * 3.141592653589793 / 180.0)
return BRepBuilderAPI_Transform(shape, t, True).Shape()
def main():
args = [a for a in sys.argv[1:] if a != "--flat"]
flat = "--flat" in sys.argv # outline mode: top-face geometry only, no relief
in_step, up, name, out_step = args[0:4]
line2 = args[4] if len(args) > 4 else ""
up = up.lower().strip()
if up not in ("y", "z"):
up = "z"
chip = read_step(in_step)
xmin, ymin, zmin, xmax, ymax, zmax = bbox(chip)
# In-plane axes (width along X always; depth is the other horizontal axis),
# and the "up" coordinate of the top face.
if up == "z":
width, depth, top = (xmax - xmin), (ymax - ymin), zmax
cx, cyz = (xmin + xmax) / 2, (ymin + ymax) / 2
thickness = zmax - zmin
else: # y-up
width, depth, top = (xmax - xmin), (zmax - zmin), ymax
cx, cyz = (xmin + xmax) / 2, (zmin + zmax) / 2
thickness = ymax - ymin
# Laser-mark relief: a shallow SURFACE mark, not deep engraving — ~1-2% of
# the chip's thickness. Kept thin on purpose so (a) the shaded iso reads like
# a real laser mark and (b) the HLR outline shows essentially just the top
# edge of the text (the sub-pixel side walls don't clutter the line art).
relief = min(0.04, max(0.012, thickness * 0.018))
lines = [name] + ([line2] if line2 else [])
n = len(lines)
margin_w, margin_d = width * 0.82, depth * 0.78
# second (variant) line gets ~70% the height of the main line
weights = [1.0, 0.7][:n]
wsum = sum(weights)
gap = margin_d * 0.12
# Build, size and stack the lines (centered block in the XY plane).
builder = BRep_Builder()
comp = TopoDS_Compound(); builder.MakeCompound(comp)
total_h = sum((margin_d - gap * (n - 1)) * (wt / wsum) for wt in weights) + gap * (n - 1)
y_cursor = total_h / 2.0
for line, wt in zip(lines, weights):
line_h = (margin_d - gap * (n - 1)) * (wt / wsum)
size = fit_size(line, margin_w, line_h)
sh, w, h, bx, by = text_shape(line, size)
# center this line horizontally; place its vertical center at y_cursor-line_h/2
cx_text = bx + w / 2.0
cy_text = by + h / 2.0
target_y = y_cursor - line_h / 2.0
sh = translate(sh, -cx_text, target_y - cy_text, 0)
builder.Add(comp, sh)
y_cursor -= (line_h + gap)
# FLAT mode (for the line-art outline): keep the text as flat top-face
# geometry floating a hair above the top so HLR draws ONLY the top edge of
# each glyph — no extrusion side walls cluttering the wireframe.
# SOLID mode (for the shaded iso): extrude a shallow relief so the mark reads
# as raised when lit.
lift = 0.005
if flat:
body = comp
else:
body = BRepPrimAPI_MakePrism(comp, gp_Vec(0, 0, relief)).Shape()
# Orient onto the top face and translate to the face center.
if up == "z":
placed = translate(body, cx, cyz, top + (lift if flat else -relief * 0.25))
else:
# Rotate local +Z normal → +Y (top face for y-up), move to the top, then
# spin 180° about the vertical (Y) axis so the glyph fronts face the iso
# camera and the part number reads left-to-right (a proper rotation —
# mirroring would invert the solid and break the boolean).
placed = rotate_x(body, -90)
placed = translate(placed, cx, top + (lift if flat else -relief * 0.25), cyz)
placed = rotate_about(placed, cx, top, cyz, 0, 1, 0, 180)
if flat:
# Don't boolean a 2D face into a solid; emit chip + text faces as a compound.
out = TopoDS_Compound(); builder.MakeCompound(out)
builder.Add(out, chip); builder.Add(out, placed)
else:
out = BRepAlgoAPI_Fuse(chip, placed).Shape()
w = STEPControl_Writer()
w.Transfer(out, STEPControl_StepModelType.STEPControl_AsIs)
if w.Write(out_step) != IFSelect_RetDone:
raise SystemExit("ERROR: STEP write failed")
kind = "flat-marked" if flat else "embossed"
print(f"OK: {kind} '{name}'" + (f" + '{line2}'" if line2 else "") + f" ({up}-up) → {out_step}")
if __name__ == "__main__":
main()