main
Caleb Publish 0.1.1 69af80b 1d ago
#!/usr/bin/env python3
"""Adom-built 3D model of TE Connectivity 2-1445098-3 (Micro MATE-N-LOK, 3 position, 3.00 mm, right angle,
plastic holddown), from TE customer drawing C-1445098 rev C (12 Dec 2025), sheets 1 and 2. CC-BY-SA-4.0.

TE's own STEP is not used: te.com terms of use allow personal, non-commercial use only and forbid redistribution.

Frame (Adom standard): Z-up, seating plane (bottom of the standoff) on z = 0, footprint frame with identity binding:
contacts at x = -3, 0, +3 (circuit 1 at x = -3), contact row on y = 0, mating face toward +y (KiCad -y),
holddown peg centred at (0, +4.32).

Dimensions (drawing values; (REF) = reference dimension; 'measured' = undimensioned, measured off the 300 dpi
render of the drawing at 47.3 px/mm, calibrated on the printed 3.00 pitch / 3.86 / 8.92 / 7.88 / DIM Y):
  housing width DIM Y 10.00 (3 POS table) ; overall over side latches DIM W 13.14 +-0.39
  housing block height 3.86 ; standoff 0.51 below it (seat) ; latch ramp 1.30 above the housing top
  mating face to contact row (8.92) REF ; holddown 4.32 from the contact row (PCB layout) ; peg tip (7.88) REF below
  the housing top ; contact 0.64 square (0.64) REF ; DIM X 3.30 +-0.15 over the holddown barbs (3 POS)
  measured: housing depth 9.24, cavity 2.60 x 2.60 at 0.63 from top, keying chamfers 0.70 (circuit 3 bottom-left,
  circuit 2 bottom-right, circuit 1 square, sheet 2 3 POS view), latch ramp 1.84 long + 1.08 flat, 2.60 wide,
  side latch neck 0.42 x 1.00, flange 0.36 x 2.00, nose to 1.57 tapering to 1.10, 2.47 deep at 4.29 from the face,
  standoff 3.55 wide from 1.42 to 7.76 from the face, peg 2.09 deep, legs 2.73 wide with a 0.68 split, barbs from
  1.80 to 2.26 below the seat, tail tips 3.06 below the seat, rear contact pocket 1.14 deep from z 1.12 to 3.69.
Not shown on the drawing (cosmetic, stated in provenance): cavity depth 6.0 mm from the face; mating pin length.
"""
import sys
import cadquery as cq

P = 3.00                  # pitch
XS = (-P, 0.0, P)         # circuit 1, 2, 3
ROW_TO_FACE = 8.92        # contact row to mating face (REF)
DEPTH = 9.24              # housing depth, face to rear (measured)
Y_FACE = ROW_TO_FACE      # mating face at +y
Y_REAR = ROW_TO_FACE - DEPTH   # -0.32
W = 10.00                 # DIM Y
Z_SO = 0.51               # standoff height
Z_TOP = Z_SO + 3.86       # 4.37
Z_C = Z_TOP - 0.63 - 1.30  # cavity / contact centre height (cavity 2.60 tall, 0.63 below the top) = 2.44
T = 0.64                  # contact square


def box(x0, x1, y0, y1, z0, z1):
    return cq.Workplane('XY').box(x1 - x0, y1 - y0, z1 - z0, centered=False).translate((x0, y0, z0))


def housing():
    h = box(-W / 2, W / 2, Y_REAR, Y_FACE, Z_SO, Z_TOP)
    # cavities from the mating face, 2.60 square, keying chamfers 0.70 (circuit 3 bottom-left, 2 bottom-right)
    cav_d = 6.0
    for i, x in enumerate(XS):
        a = 1.30; c = 0.70
        pts = [(-a, -a), (a, -a), (a, a), (-a, a)]
        if i == 1:   # circuit 2: bottom-right as seen from the front. Front view looks along -y: its right is -x.
            pts = [(-a + c, -a), (a, -a), (a, a), (-a, a), (-a, -a + c)]
        if i == 2:   # circuit 3: bottom-left as seen from the front = +x side
            pts = [(-a, -a), (a - c, -a), (a, -a + c), (a, a), (-a, a)]
        # sketch in the XZ plane at the face, then cut toward -y
        wp = cq.Workplane('XZ', origin=(0, Y_FACE, 0)).polyline([(x + u, Z_C + v) for u, v in pts]).close()
        h = h.cut(wp.extrude(cav_d))      # XZ normal is -y: extrude goes into the housing
    # rear pockets where the contacts leave the housing
    for x in XS:
        h = h.cut(box(x - 0.80, x + 0.80, Y_REAR - 0.01, Y_REAR + 1.14, 1.12, 3.69))
    # latch ramp on top, centred, 2.60 wide: ramp 1.84 from the face up 1.30, flat 1.08, vertical drop, r 0.57 fillet into the top
    prof = (cq.Workplane('YZ', origin=(-1.30, 0, 0))
            .moveTo(Y_FACE, Z_TOP).lineTo(Y_FACE - 1.84, Z_TOP + 1.30).lineTo(Y_FACE - 2.92, Z_TOP + 1.30)
            .lineTo(Y_FACE - 2.92, Z_TOP + 0.57)
            .threePointArc((Y_FACE - 3.49 + 0.403, Z_TOP + 0.57 - 0.403), (Y_FACE - 3.49, Z_TOP))   # r 0.57 fillet
            .close())
    h = h.union(prof.extrude(2.60))
    # side latches (both sides), front-view profile extruded 2.47 deep, centred 4.29 behind the face
    yc, d = Y_FACE - 4.29, 2.47
    for s in (1, -1):
        pts = [(5.00, -0.50), (5.42, -0.50), (5.42, -1.00), (5.78, -1.00), (6.57, -0.55), (6.57, 0.55),
               (5.78, 1.00), (5.42, 1.00), (5.42, 0.50), (5.00, 0.50)]
        pts = [(s * u, Z_C + v) for u, v in pts]
        wp = cq.Workplane('XZ', origin=(0, yc + d / 2, 0)).polyline(pts).close()
        h = h.union(wp.extrude(d))
    # standoff under the housing
    h = h.union(box(-1.775, 1.775, Y_FACE - 7.76, Y_FACE - 1.42, 0.0, Z_SO + 0.01))
    return h


def holddown():
    yc = 4.32                     # from the contact row toward the face
    zb0, zb1, ztip = -1.80, -2.26, -3.51
    # front-view (XZ) profile of the split peg: legs 2.73 wide, barbs to 3.30, taper to the tip
    xz = [(-1.365, 0.05), (-1.365, zb0), (-1.65, zb0), (-1.65, zb1), (-0.48, ztip),
          (0.48, ztip), (1.65, zb1), (1.65, zb0), (1.365, zb0), (1.365, 0.05)]
    a = cq.Workplane('XZ', origin=(0, yc + 1.045, 0)).polyline(xz).close().extrude(2.09)
    # side-view (YZ) profile: 2.09 deep, tip chamfered from the barb to 0.3 wide
    yz = [(-1.045, 0.05), (-1.045, zb1), (-0.30, ztip), (0.30, ztip), (1.045, zb1), (1.045, 0.05)]
    b = cq.Workplane('YZ', origin=(-2.0, 0, 0)).polyline([(yc + u, v) for u, v in yz]).close().extrude(4.0)
    peg = a.intersect(b)
    # 0.68 split between the legs, round-topped at z +0.45 (inside the standoff)
    slot = box(-0.34, 0.34, yc - 1.2, yc + 1.2, ztip - 0.1, 0.11)
    slot = slot.union(cq.Workplane('XZ', origin=(0, yc + 1.2, 0)).center(0, 0.11).circle(0.34).extrude(2.4))
    return peg.cut(slot)


def contact(x):
    """0.64 square contact: mating pin in the cavity, out through the rear pocket, S-bend round the lower lip,
    tail down at the contact row (y = 0) to 3.06 below the seat."""
    pts = [(Y_FACE - 1.50, Z_C), (0.10, Z_C), (-0.62, 1.70), (-0.62, 0.90), (0.0, 0.25), (0.0, -3.06 + 0.20)]
    path = cq.Workplane('YZ', origin=(x, 0, 0)).polyline(pts)
    prof = cq.Workplane(cq.Plane(origin=(x, pts[0][0], pts[0][1]), xDir=(1, 0, 0), normal=(0, -1, 0))).rect(T, T)
    c = prof.sweep(path, transition='right')
    # chamfered tail tip
    tip = (cq.Workplane('XY', origin=(x, 0, -3.06 + 0.20)).rect(T, T).workplane(offset=-0.20).rect(0.36, 0.36)
           .loft())
    return c.union(tip)


def build():
    body = housing().union(holddown())
    pins = None
    for x in XS:
        c = contact(x)
        pins = c if pins is None else pins.union(c)
    asm = cq.Assembly(name='2-1445098-3')
    asm.add(body, name='housing', color=cq.Color(0.08, 0.08, 0.08))
    asm.add(pins, name='contacts', color=cq.Color(0.78, 0.74, 0.60))
    return asm, body, pins


if __name__ == '__main__':
    out = sys.argv[1] if len(sys.argv) > 1 else '2-1445098-3.step'
    asm, body, pins = build()
    asm.save(out, 'STEP')
    bb = body.union(pins).val().BoundingBox()
    print(f'bbox x {bb.xmin:.3f}..{bb.xmax:.3f} ({bb.xlen:.3f})  y {bb.ymin:.3f}..{bb.ymax:.3f} ({bb.ylen:.3f})  '
          f'z {bb.zmin:.3f}..{bb.zmax:.3f} ({bb.zlen:.3f})')