app
[DEPRECATED] Chip Fetcher → adom-chip-fetcher
Public Made by Adomby adom
DEPRECATED — use adom/adom-chip-fetcher. No longer maintained.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
#!/usr/bin/env python3
"""Generate black-and-white vector OUTLINE icons of a chip STEP via OCCT HLR
(hidden-line removal), at the user's chosen orientation.
Usage: occt-outline.py <step> <outdir> <mpn> [axis]
Writes <mpn>-3d-outline-<style>.svg for each style (thin/bold/blueprint/teal/dark),
rendered at the same camera as chip-thumbnailer's iso pose for <axis>
(z/asIs, y/yUp, x/xUp, -y/yDown, -x/xDown, -z/zDown). Requires cadquery-ocp (OCP).
"""
import sys, os, math
from OCP.STEPControl import STEPControl_Reader
from OCP.HLRBRep import HLRBRep_Algo, HLRBRep_HLRToShape
from OCP.HLRAlgo import HLRAlgo_Projector
from OCP.gp import gp_Pnt, gp_Dir, gp_Ax2, gp_Ax3, gp_Trsf
from OCP.Bnd import Bnd_Box
from OCP.BRepBndLib import BRepBndLib
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_EDGE
from OCP.TopoDS import TopoDS
from OCP.BRepAdaptor import BRepAdaptor_Curve
from OCP.GeomAbs import GeomAbs_Line
from OCP.BRepBuilderAPI import BRepBuilderAPI_Transform
try:
from hershey_simplex import FONT as HERSHEY
except Exception:
import os as _os
sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__)))
from hershey_simplex import FONT as HERSHEY
# Single-stroke text: one line of Hershey glyphs → list of 2D polylines (font
# units; x advances, baseline y=0, left-origin). Returns (polylines, width).
def _hershey_line(text):
polys = []
cx = 0.0
for ch in text:
g = HERSHEY.get(ord(ch)) or HERSHEY.get(63) # '?' fallback
w = g[0]
coords = g[1:]
cur = []
i = 0
while i < len(coords):
if coords[i] is None:
if len(cur) >= 2:
polys.append(cur)
cur = []
i += 1
continue
cur.append((cx + coords[i], coords[i + 1]))
i += 2
if len(cur) >= 2:
polys.append(cur)
cx += w
return polys, cx
CAP = 21.0 # Hershey cap height in font units
# chip-thumbnailer's _UP_AXIS_VIEW (proj, up) per orientation.
AXIS_VIEW = {
"asis": ((1, 1, 1), (0, 0, 1)),
"zdown": ((1, -1, -1), (0, 0, -1)),
"yup": ((1, 1, -1), (0, 1, 0)),
"ydown": ((1, -1, 1), (0, -1, 0)),
"xup": ((1, 1, -1), (1, 0, 0)),
"xdown": ((-1, 1, 1), (-1, 0, 0)),
}
ALIAS = {"z": "asis", "zup": "asis", "": "asis", "asis": "asis",
"y": "yup", "yup": "yup", "-y": "ydown", "ydown": "ydown",
"x": "xup", "xup": "xup", "-x": "xdown", "xdown": "xdown",
"-z": "zdown", "zdown": "zdown"}
def nrm(v): n = math.sqrt(sum(c*c for c in v)) or 1; return tuple(c/n for c in v)
def dot(a, b): return sum(x*y for x, y in zip(a, b))
def cross(a, b): return (a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0])
def view_trsf(proj, up):
d = nrm((-proj[0], -proj[1], -proj[2]))
u = nrm((up[0]-dot(up, d)*d[0], up[1]-dot(up, d)*d[1], up[2]-dot(up, d)*d[2]))
r = nrm(cross(d, u))
ax3 = gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(-d[0], -d[1], -d[2]), gp_Dir(*r))
t = gp_Trsf(); t.SetTransformation(ax3)
return t
def to_view(shape, proj, up):
return BRepBuilderAPI_Transform(shape, view_trsf(proj, up), True).Shape()
# Build single-stroke name text, placed flat on the chip's TOP face (per up-axis)
# and projected through the SAME view transform as the chip → 2D polylines in the
# chip's projected space. `lines` is a list of strings (line 2 = variant suffix).
def name_polylines_2d(orig_shape, up, lines, proj, vproj):
b = Bnd_Box(); BRepBndLib.Add_s(orig_shape, b)
xmin, ymin, zmin, xmax, ymax, zmax = b.Get()
if up == "y":
width, depth, top = (xmax - xmin), (zmax - zmin), ymax
cu, cv = (xmin + xmax) / 2, (zmin + zmax) / 2
else: # z-up / as-is
width, depth, top = (xmax - xmin), (ymax - ymin), zmax
cu, cv = (xmin + xmax) / 2, (ymin + ymax) / 2
lines = [l for l in lines if l]
n = len(lines) or 1
# Fit: widest line spans ~74% of the face width; all lines stack within ~46%
# of the depth. Scale = font-units → mm.
raw = [(_hershey_line(s)) for s in lines]
maxw = max((w for _, w in raw), default=1) or 1
line_gap = CAP * 0.5
block_h = n * CAP + (n - 1) * line_gap
sc = min(width * 0.74 / maxw, depth * 0.46 / block_h)
t = view_trsf(proj, vproj)
out = []
# stack lines top→down (first line highest)
y_cursor = (block_h / 2.0) # in font units, centered block
for polys, w in raw:
# center this line horizontally; baseline so the line's cap-center sits at y_cursor-CAP/2
x_off = -w / 2.0
y_base = (y_cursor - CAP) # baseline of this line (font units)
for poly in polys:
pts3 = []
for (fx, fy) in poly:
lx = (fx + x_off) * sc
ly = (fy + y_base) * sc
if up == "y":
P = gp_Pnt(cu - lx, top + 0.002, cv + ly)
else:
P = gp_Pnt(cu + lx, cv + ly, top + 0.002)
P.Transform(t)
pts3.append((P.X(), P.Y()))
if len(pts3) >= 2:
out.append(pts3)
y_cursor -= (CAP + line_gap)
return out
def polylines(comp, n=22):
out = []
if comp is None or comp.IsNull(): return out
exp = TopExp_Explorer(comp, TopAbs_EDGE)
while exp.More():
try:
ad = BRepAdaptor_Curve(TopoDS.Edge_s(exp.Current()))
a, b = ad.FirstParameter(), ad.LastParameter()
steps = 1 if ad.GetType() == GeomAbs_Line else n
pts = [(ad.Value(a+(b-a)*i/steps).X(), ad.Value(a+(b-a)*i/steps).Y()) for i in range(steps+1)]
if len(pts) >= 2: out.append(pts)
except Exception: pass
exp.Next()
return out
def hlr(shape):
ax2 = gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1), gp_Dir(1, 0, 0))
a = HLRBRep_Algo(); a.Add(shape); a.Projector(HLRAlgo_Projector(ax2)); a.Update(); a.Hide()
hs = HLRBRep_HLRToShape(a)
def g(nm):
try: return getattr(hs, nm)()
except Exception: return None
return {"vis": polylines(g("VCompound")), "hid": polylines(g("HCompound"))}
def svg(groups, vb, size=320, pad=0.10, bg=None):
minx, miny, maxx, maxy = vb; w = (maxx-minx) or 1; h = (maxy-miny) or 1
s = size*(1-2*pad)/max(w, h)
ox = size*pad - minx*s + (size*(1-2*pad)-w*s)/2
oy = size*pad + maxy*s + (size*(1-2*pad)-h*s)/2
def P(poly): return " ".join(f"{ox+x*s:.2f},{oy-y*s:.2f}" for x, y in poly)
body = f'<rect width="{size}" height="{size}" fill="{bg}"/>' if bg else ''
for polys, st in groups:
for poly in polys: body += f'<polyline points="{P(poly)}" {st}/>'
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {size} {size}">{body}</svg>'
def main():
# Optional --suffix <s> appends to the output filenames (e.g. "-named") so a
# name-marked variant can sit beside the plain outline.
argv = sys.argv[1:]
suffix = ""
name = ""
line2 = ""
for flag, setter in (("--suffix", "suffix"), ("--name", "name"), ("--line2", "line2")):
if flag in argv:
i = argv.index(flag); val = argv[i + 1]; del argv[i:i + 2]
if setter == "suffix": suffix = val
elif setter == "name": name = val
else: line2 = val
step, outdir, mpn = argv[0], argv[1], argv[2]
axis = ALIAS.get((argv[3] if len(argv) > 3 else "").lower(), "asis")
proj, up = AXIS_VIEW[axis]
r = STEPControl_Reader(); r.ReadFile(step); r.TransferRoots()
orig = r.OneShape()
shape = to_view(orig, proj, up)
H = hlr(shape)
# Single-stroke part-number on the top face (centerline text, not outlined
# glyphs — reads cleanly even for long names).
nm = name_polylines_2d(orig, "y" if axis == "yup" else "z", [name, line2], proj, up) if name else []
xs = [x for g in (H["vis"], H["hid"]) for poly in g for x, y in poly]
ys = [y for g in (H["vis"], H["hid"]) for poly in g for x, y in poly]
vb = (min(xs), min(ys), max(xs), max(ys)) if xs else (0, 0, 1, 1)
J = 'stroke-linejoin="round" stroke-linecap="round"'
# (visible-edge stroke, name stroke). Name is a touch thinner so the
# centerline text stays crisp against the chip's silhouette.
styles = {
"thin": (("#0d1117", "1.1", "#0d1117", "0.9"), None),
"bold": (("#0d1117", "2.4", "#0d1117", "1.6"), None),
"teal": (("#00e6dc", "1.7", "#00e6dc", "1.2"), None),
"dark": (("#e6edf3", "1.5", "#e6edf3", "1.1"), None),
}
for k, ((ec, ew, nc, nw), bg) in styles.items():
gs = [(H["vis"], f'fill="none" stroke="{ec}" stroke-width="{ew}" {J}')]
if nm:
gs.append((nm, f'fill="none" stroke="{nc}" stroke-width="{nw}" {J}'))
open(os.path.join(outdir, f"{mpn}-3d-outline-{k}{suffix}.svg"), "w").write(svg(gs, vb, bg=bg))
# blueprint = WHITE visible lines + faint blue dashed hidden lines + white name.
bp = [(H["hid"], 'fill="none" stroke="#7fa8d8" stroke-width="0.6" stroke-dasharray="3,2"'),
(H["vis"], f'fill="none" stroke="#e6edf3" stroke-width="1.3" {J}')]
if nm:
bp.append((nm, f'fill="none" stroke="#e6edf3" stroke-width="1.0" {J}'))
open(os.path.join(outdir, f"{mpn}-3d-outline-blueprint{suffix}.svg"), "w").write(svg(bp, vb, bg=None))
print(f"OK: wrote 5 outline styles for {mpn} at axis={axis} (vis edges={len(H['vis'])}, name strokes={len(nm)})")
if __name__ == "__main__":
main()