component
AS5047P-ATSM
Public Made by Adomby adom
AMS Position Sensors, TSSOP-14. Pinout extracted from the datasheet, KiCad symbol/footprint/3D.
main
John Lauer
KiCad's library model checked against ams' limits and kept (foot 0.529 mm at the gauge plane, in 0.45..0.75); four LODs…
cb10da4
12d ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
#!/usr/bin/env python3
"""Overlay proof: an orthographic render of a STEP blended over a datasheet drawing view.
usage: overlay.py spec.json
spec: {"step": "...", "out": "...png", "body": {"x": half, "y": half, "zmin": z}, # body box for the inner outline
"views": [{"page": "png", "crop": [x0,y0,x1,y1], "view": "top"|"front"|"side", "rot": deg (top only),
"flip": false, "centre": [px,py] (page px of model origin: top=body centre, front/side=(centre, seating plane)),
"scale": px_per_mm, "title": "..."}],
"caption": ["line", ...]}
front = looking along +Y (shows X horizontally, Z up); side = looking along -X (shows Y, Z)."""
import sys, json, math
import numpy as np
import cadquery as cq
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageChops
spec = json.load(open(sys.argv[1]))
shape = cq.importers.importStep(spec["step"]).val()
verts, tris = shape.tessellate(0.005, 0.2)
V = np.array([[v.x, v.y, v.z] for v in verts]); T = np.array(tris)
bx = spec.get("body")
def proj(view, rot=0):
if view == "top":
a = math.radians(rot); c, s = math.cos(a), math.sin(a)
return np.stack([c * V[:, 0] - s * V[:, 1], s * V[:, 0] + c * V[:, 1]], 1)
if view == "front":
return np.stack([V[:, 0], V[:, 2]], 1)
if view == "side":
return np.stack([V[:, 1], V[:, 2]], 1)
def body_tris():
if not bx: return np.zeros(len(T), bool)
c = V[T].mean(1)
return (np.abs(c[:, 0]) <= bx["x"] + 1e-3) & (np.abs(c[:, 1]) <= bx["y"] + 1e-3) & (c[:, 2] >= bx["zmin"] - 1e-3)
BT = body_tris()
SS = 4
panels = []
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 22)
small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 18)
except Exception:
font = small = ImageFont.load_default()
for vw in spec["views"]:
page = Image.open(vw["page"]).convert("L")
x0, y0, x1, y1 = vw["crop"]
W, H = x1 - x0, y1 - y0
P = proj(vw["view"], vw.get("rot", 0))
if vw.get("flip"): P[:, 0] = -P[:, 0]
s = vw["scale"]; cx, cy = vw["centre"]
px = (cx - x0 + P[:, 0] * s) * SS; py = (cy - y0 - P[:, 1] * s) * SS
def mask(sel):
m = Image.new("L", (W * SS, H * SS), 0); d = ImageDraw.Draw(m)
for t in T[sel]:
d.polygon([(px[i], py[i]) for i in t], fill=255)
return m.resize((W, H), Image.LANCZOS).point(lambda v: 255 if v > 127 else 0)
m_all = mask(np.ones(len(T), bool))
m_body = mask(BT) if BT.any() and vw["view"] == "top" else None
def edge(m):
return ImageChops.subtract(m, m.filter(ImageFilter.MinFilter(3)))
base = page.crop((x0, y0, x1, y1)).convert("RGB")
tint = Image.new("RGB", (W, H), (0, 170, 220))
out = Image.composite(Image.blend(base, ImageChops.multiply(base, tint), 0.55), base, m_all)
red = Image.new("RGB", (W, H), (230, 30, 30))
out = Image.composite(red, out, edge(m_all))
if m_body is not None:
out = Image.composite(Image.new("RGB", (W, H), (240, 120, 0)), out, edge(m_body))
hdr = 34
pan = Image.new("RGB", (W, H + hdr), "white"); pan.paste(out, (0, hdr))
ImageDraw.Draw(pan).text((6, 6), vw.get("title", vw["view"]), fill="black", font=font)
panels.append(pan)
cap = spec.get("caption", [])
Wt = max(sum(p.width for p in panels) + 20 * (len(panels) + 1), 40 + max([int(small.getlength(c)) for c in cap] + [0]))
Ht = max(p.height for p in panels) + 40 + 26 * len(cap)
sheet = Image.new("RGB", (Wt, Ht), "white"); x = 20
for p in panels:
sheet.paste(p, (x, 20)); x += p.width + 20
d = ImageDraw.Draw(sheet); y = max(p.height for p in panels) + 30
for line in cap:
d.text((20, y), line, fill="black", font=small); y += 26
sheet.save(spec["out"])
print("wrote", spec["out"], sheet.size)