master
John Lauer Laser mark by the planner (adom-aiflow 0.1.49): re-etched, outlines regenerated, GLB / hero / outline symbol rebuilt; l… 987efbc 17d ago
"""Bourns CR0402 thick film chip resistor 3D model built from the datasheet dimensions (AI-generated, 2026-10-03).

Bourns publishes no 3D model for the CR series (bourns.com carries the datasheet only; the Ultra Librarian
and ComponentSearchEngine models are third-party builds), so this is built with OpenCascade (OCP) from the
Bourns CR Series datasheet REV. 03/21, p.2 "Product Dimensions" (CR0402 row, nominal values):

  L 1.00 +/- 0.05   W 0.50 +/- 0.05   T 0.32 +/- 0.05   C (top terminal) 0.20 +/- 0.10   D (bottom terminal) 0.25 +/- 0.10

Construction (real chip resistor anatomy, plating thickness 0.012 mm is illustrative):
  - white alumina substrate between the end caps;
  - black protective overcoat on top between the top terminals (0.60 long = L - 2C), reaching T;
  - two tin-plated wrap-around terminals: top pad C long, the end face, bottom pad D long.

Frame: mm, Z up, seated on z = 0, origin at the package centre, length along X (pad 1 at -X, matching the
page footprint). Usage: build_cr0402.py OUT.step REPORT.json [--name MPN]"""
import sys, json
from OCP.STEPCAFControl import STEPCAFControl_Writer
from OCP.STEPControl import STEPControl_AsIs
from OCP.TDocStd import TDocStd_Document
from OCP.TCollection import TCollection_ExtendedString
from OCP.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorType
from OCP.TDataStd import TDataStd_Name
from OCP.Quantity import Quantity_Color, Quantity_TOC_sRGB
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCP.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCP.gp import gp_Pnt
from OCP.TopLoc import TopLoc_Location
from OCP.Interface import Interface_Static

out, rp = sys.argv[1:3]
NAME = sys.argv[sys.argv.index('--name') + 1] if '--name' in sys.argv else 'CR0402'
L, W, T, C, D = 1.00, 0.50, 0.32, 0.20, 0.25      # datasheet p.2, CR0402 nominal
P = 0.012                                          # plating thickness (illustrative)
ZS = 0.29                                          # substrate top; overcoat rises to T
TIN, ALUMINA, OVERCOAT = (0.80, 0.81, 0.83), (0.94, 0.94, 0.92), (0.06, 0.06, 0.07)


def box(x0, y0, z0, x1, y1, z1): return BRepPrimAPI_MakeBox(gp_Pnt(x0, y0, z0), x1 - x0, y1 - y0, z1 - z0).Shape()
def fuse(a, b): return BRepAlgoAPI_Fuse(a, b).Shape()


def terminal(s):  # s = -1 (pad 1, -X) or +1 (pad 2, +X): top pad C, end face, bottom pad D
    x_end = s * L / 2
    face = box(min(x_end, x_end - s * P), -W / 2, 0, max(x_end, x_end - s * P), W / 2, ZS + P)
    top = box(min(x_end, x_end - s * C), -W / 2, ZS, max(x_end, x_end - s * C), W / 2, ZS + P)
    bot = box(min(x_end, x_end - s * D), -W / 2, 0, max(x_end, x_end - s * D), W / 2, P)
    return fuse(fuse(face, top), bot)


parts = [
    ('alumina_substrate', box(-L / 2 + P, -W / 2, P, L / 2 - P, W / 2, ZS), ALUMINA),
    ('overcoat', box(-L / 2 + C, -W / 2 + 0.02, ZS, L / 2 - C, W / 2 - 0.02, T), OVERCOAT),
    ('terminal_1', terminal(-1), TIN),
    ('terminal_2', terminal(+1), TIN),
]
doc = TDocStd_Document(TCollection_ExtendedString("d"))
st = XCAFDoc_DocumentTool.ShapeTool_s(doc.Main()); ct = XCAFDoc_DocumentTool.ColorTool_s(doc.Main())
asm = st.NewShape(); TDataStd_Name.Set_s(asm, TCollection_ExtendedString(NAME))
for name, shp, rgb in parts:
    lab = st.AddShape(shp, False); TDataStd_Name.Set_s(lab, TCollection_ExtendedString(name))
    q = Quantity_Color(rgb[0], rgb[1], rgb[2], Quantity_TOC_sRGB)
    ct.SetColor(lab, q, XCAFDoc_ColorType.XCAFDoc_ColorGen); ct.SetColor(lab, q, XCAFDoc_ColorType.XCAFDoc_ColorSurf)
    comp = st.AddComponent(asm, lab, TopLoc_Location()); TDataStd_Name.Set_s(comp, TCollection_ExtendedString(name))
st.UpdateAssemblies()
Interface_Static.SetCVal_s("write.step.schema", "AP214IS"); Interface_Static.SetCVal_s("write.step.unit", "MM")
wr = STEPCAFControl_Writer(); wr.SetColorMode(True); wr.SetNameMode(True); wr.SetLayerMode(True)
wr.Transfer(doc, STEPControl_AsIs); assert wr.Write(out) == 1
hdr = ("/* %s (Bourns CR0402 thick film chip resistor) 3D model - AI-generated (Claude, Adom) from the Bourns CR Series\n"
       "   datasheet REV. 03/21 p.2 Product Dimensions (L 1.00, W 0.50, T 0.32, C 0.20, D 0.25 mm), with OpenCascade (OCP).\n"
       "   Not a manufacturer model: Bourns publishes none for the CR series. mm, Z up, seated on z = 0, pad 1 at -X. */" % NAME)
t = open(out, encoding='latin-1').read().replace('HEADER;', 'HEADER;\n' + hdr, 1); open(out, 'w', encoding='latin-1').write(t)
json.dump(dict(tool='build_cr0402.py', name=NAME, doc=__doc__, source=dict(doc="Bourns CR Series Thick Film Chip Resistors datasheet REV. 03/21",
               url="https://www.bourns.com/docs/product-datasheets/cr.pdf", page=2, table="Product Dimensions, CR0402 row"),
               dims_mm=dict(L=L, W=W, T=T, C=C, D=D, plating=P, substrate_top=ZS),
               parts=[dict(name=n, srgb=list(c)) for n, _, c in parts]), open(rp, 'w'), indent=1)
print(json.dumps(dict(out=out, name=NAME)))