rushabhcodes/usb-audio-dac

A tscircuit package created by rushabhcodes.

Version
1.1.2
License
unset
Stars
0

enclosure.tsx

import { assembly, jscad } from "tscircuit"

/** Existing optional PCB holes; the snap-fit cover does not need screws. */
export const mountingPoints = [[-23.5, -12.5], [-23.5, 12.5], [23.5, -12.5], [23.5, 12.5]] as const

/** Millimetres in the PCB frame: board centre z=0, surfaces z=±0.8. */
export const enclosureDimensions = {
  width: 58,
  length: 36,
  wall: 1.6,
  floorBottom: -3.8,
  floorTop: -2.2,
  seam: 6.7,
  lidTop: 8.3,
  skirtBottom: 1.05,
  skirtThickness: 0.8,
  fitClearance: 0.2,
} as const

const d = enclosureDimensions
const innerWidth = d.width - 2 * d.wall
const innerLength = d.length - 2 * d.wall
const skirtWidth = innerWidth - 2 * d.fitClearance
const skirtLength = innerLength - 2 * d.fitClearance
const snapPositions = [-15, 15] as const
const sides = [-1, 1] as const

function Box({ width, length, bottom, top, x = 0, y = 0 }: {
  width: number; length: number; bottom: number; top: number; x?: number; y?: number
}) {
  return <jscad.cuboid size={[width, length, top - bottom]} center={[x, y, (top + bottom) / 2]} />
}

/** Cuts both the wall and lid skirt. Datums follow the current connector models. */
function PortOpenings({ openTop = false }: { openTop?: boolean }) {
  return <>{[
    { x: -28, z: 2.4255, width: 12.6, height: 7.6, radius: 3.8 },
    { x: 28, z: 3.25, width: 10, height: 6.5, radius: 0.8 },
  ].map(({ x, z, width, height, radius }) => <jscad.union key={x}>
    <jscad.hull>
      {sides.flatMap((ySide) => sides.map((zSide) => <jscad.cylinder key={`${ySide}/${zSide}`}
        radius={radius} height={6} center={[x, ySide * (width / 2 - radius), z + zSide * (height / 2 - radius)]}
        rotation={[0, Math.PI / 2, 0]} />))}
    </jscad.hull>
    {/* The lid closes these U-shaped base openings, avoiding thin port bridges. */}
    {openTop && <Box x={x} width={6} length={width} bottom={z} top={d.seam + 0.1} />}
  </jscad.union>)}</>
}

/** A ramp deflects each lid tongue inward; its flat shoulder catches the window. */
function SnapHook({ x, side }: { x: number; side: number }) {
  const points: [number, number][] = [[16, 1.1], [16.7, 2.05], [16, 2.05]]
    .map(([y, z]) => [side * y, z])
  if (side < 0) points.reverse()
  return <jscad.translate offset={[x - 3, 0, 0]}>
    <jscad.rotate angles={[Math.PI / 2, 0, Math.PI / 2]}>
      <jscad.extrudeLinear height={6}><jscad.polygon points={points} /></jscad.extrudeLinear>
    </jscad.rotate>
  </jscad.translate>
}

/** Two printable parts: a shallow tray with PCB ledges and a snap-on lid. */
export function SnapFitEnclosure() {
  return <>
    <assembly.printedpart name="CaseBase" color="#555555" material="petg" jscad={
      <jscad.subtract>
        <jscad.union>
          <jscad.subtract>
            <Box width={d.width} length={d.length} bottom={d.floorBottom} top={d.seam} />
            <Box width={innerWidth} length={innerLength} bottom={d.floorTop} top={d.seam + 0.1} />
          </jscad.subtract>
          {/* Four edge ledges support the PCB underside; no posts or carrier. */}
          {[-23.5, 23.5].flatMap((x) => sides.map((side) =>
            <Box key={`${x}/${side}`} x={x} y={side * 15.4} width={5} length={3}
              bottom={d.floorTop - 0.1} top={-0.8} />))}
        </jscad.union>
        <PortOpenings openTop />
        {/* Small through-windows also let a thin tool release the four hooks. */}
        {snapPositions.flatMap((x) => sides.map((side) =>
          <Box key={`${x}/${side}`} x={x} y={side * 17.1} width={6.6} length={2.2}
            bottom={0.9} top={2.25} />))}
      </jscad.subtract>
    } />
    <assembly.printedpart name="CaseLid" color="#777777" material="petg" jscad={
      <jscad.subtract>
        <jscad.union>
          <Box width={d.width} length={d.length} bottom={d.seam} top={d.lidTop} />
          <jscad.subtract>
            <Box width={skirtWidth} length={skirtLength} bottom={d.skirtBottom} top={d.seam + 0.1} />
            <Box width={skirtWidth - 2 * d.skirtThickness} length={skirtLength - 2 * d.skirtThickness}
              bottom={d.skirtBottom - 0.1} top={d.seam + 0.2} />
          </jscad.subtract>
          {snapPositions.flatMap((x) => sides.map((side) =>
            <SnapHook key={`${x}/${side}`} x={x} side={side} />))}
        </jscad.union>
        <PortOpenings />
        {/* The skirt overlaps the PCB edge, retaining it with 0.25 mm vertical play. */}
        {snapPositions.flatMap((x) => sides.flatMap((side) => sides.map((edge) =>
          <Box key={`${x}/${side}/${edge}`} x={x + edge * 3.3} y={side * 16} width={0.6} length={2}
            bottom={d.skirtBottom - 0.1} top={6.25} />)))}
      </jscad.subtract>
    } />
  </>
}