component
CX3225GA08000D0PTVTT - 8 MHz Automotive Crystal, 3.2 x 2.5 mm
Public Made by Adomby adom
Kyocera CX3225GA08000D0PTVTT: 8 MHz fundamental quartz crystal, 8 pF load, +-50 ppm, -40 to +150 C, AEC-Q200, 3.2 x 2.5 x 0.85 mm ceramic SMD with 2 pads. KiCad symbol and footprint from Kyocera's land pattern, datasheet-built 3D model, Fusion and Altium libraries.
main
John Lauer
LODs, models.json, verification log, provenance and tools, README, license
c543194
17h ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
#!/usr/bin/env python3
"""Overlay proof for the CX3225GA model: orthographic projections of the plain STEP blended over the
Kyocera catalog drawing (600 dpi render of cx3225ga_e.pdf). The drawing is not drawn to one scale, so
each view is calibrated per axis on its own printed body edges (3.2 mm and 2.5 mm, 0.85 mm height).
Views: top (whole model), front (X, Z), end (Y, Z), bottom (electrodes only; third-angle bottom view:
X kept, Y flipped, as Kyocera draws it).
usage: overlay_cx.py model.step datasheet-600dpi.png out.png"""
import sys
import numpy as np
import cadquery as cq
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageChops
step, page_png, out = sys.argv[1:4]
asm = cq.importers.importStep(step)
solids = asm.solids().vals()
def tess(sel):
V, T = [], []
for s in sel:
v, t = s.tessellate(0.002, 0.1)
o = len(V)
V += [[p.x, p.y, p.z] for p in v]
T += [[a + o, b + o, c + o] for a, b, c in t]
return np.array(V), np.array(T)
ALL = tess(solids)
# electrodes are the solids that touch z = 0
ELEC = tess([s for s in solids if s.BoundingBox().zmin < 1e-6 and s.BoundingBox().zmax < 0.3])
# (title, crop, mesh, axis_u, axis_v, sign_v, centre_px, scale_u, scale_v)
# centres and scales measured on the drawing (see provenance/3d-model.json "overlay")
VIEWS = [
("Top view (x 157, y 141.8 px/mm)", (700, 5170, 1360, 5640), ALL, 0, 1, 1, (1028.75, 5404.25), 157.0, 141.8),
("Front view (x 155.8, z 144.7 px/mm)", (700, 5700, 1360, 5930), ALL, 0, 2, 1, (1028.75, 5880.5), 155.8, 144.7),
("End view (y 128.8, z 144.7 px/mm)", (1680, 5700, 2090, 5930), ALL, 1, 2, 1, (1882.5, 5880.5), 128.8, 144.7),
("Bottom view, electrodes (x 157, y 144.8 px/mm)", (700, 6040, 1360, 6520), ELEC, 0, 1, -1, (1028.75, 6279.5), 157.0, 144.8),
]
page = Image.open(page_png).convert("L")
SS = 4
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20)
small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 18)
except Exception:
font = small = ImageFont.load_default()
panels = []
for title, (x0, y0, x1, y1), (V, T), au, av, sv, (cx, cy), su, svs in VIEWS:
W, H = x1 - x0, y1 - y0
px = (cx - x0 + V[:, au] * su) * SS
# top view: model +y is up on paper; bottom view (third angle): model +y is down on paper
py = (cy - y0 - sv * V[:, av] * svs) * SS
m = Image.new("L", (W * SS, H * SS), 0)
d = ImageDraw.Draw(m)
for t in T:
d.polygon([(px[i], py[i]) for i in t], fill=255)
m = m.resize((W, H), Image.LANCZOS).point(lambda v: 255 if v > 127 else 0)
edge = 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))
o = Image.composite(Image.blend(base, ImageChops.multiply(base, tint), 0.55), base, m)
o = Image.composite(Image.new("RGB", (W, H), (230, 30, 30)), o, edge)
pan = Image.new("RGB", (W, H + 34), "white")
pan.paste(o, (0, 34))
ImageDraw.Draw(pan).text((6, 6), title, fill="black", font=font)
panels.append(pan)
cap = ["CX3225GA08000D0PTVTT model (red outline, blue fill) over Kyocera's CX3225GA catalog drawing (As of October 2025), 600 dpi.",
"The drawing is not drawn to one scale (3.2 mm = 157 px but 2.5 mm = 142 to 145 px, and 129 px in the end view), so each view is",
"calibrated on its own printed body edges. Printed: 3.2 x 2.5 x 0.85, 0.53 base, 0.6 electrode gap, R0.3, 4-R0.15, 0.5 and 0.25 castellation.",
"Measured off the drawing: electrode height 2.28, outer arc 1.24 to 1.48 from centre, lid inset 0.10, castellation depth 0.10."]
cols = 2
rows = [panels[i:i + cols] for i in range(0, len(panels), cols)]
Wt = max(sum(p.width for p in r) + 20 * (len(r) + 1) for r in rows)
Wt = max(Wt, 40 + max(int(small.getlength(c)) for c in cap))
Ht = sum(max(p.height for p in r) + 20 for r in rows) + 30 + 26 * len(cap)
sheet = Image.new("RGB", (Wt, Ht), "white")
y = 20
for r in rows:
x = 20
for p in r:
sheet.paste(p, (x, y))
x += p.width + 20
y += max(p.height for p in r) + 20
dr = ImageDraw.Draw(sheet)
y += 10
for line in cap:
dr.text((20, y), line, fill="black", font=small)
y += 26
sheet.save(out)
print("wrote", out, sheet.size)