{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pipe-clamp",
  "type": "registry:component",
  "title": "Pipe clamp",
  "description": "Pipe clamps after MSS SP-58. Two-bolt clamp (Type 4): two formed straps with a load bolt above and a bolt below the pipe. Three-bolt clamp (Type 3): long upper ears with a load bolt and a spacer bolt, for heavier or insulated lines. Both hang on a weldless eye nut and threaded rod from a lug on a short beam. Riser clamp: two bent bars bolted across a vertical pipe with ears bearing on channel supports. Strap, bolt and rod sizes follow typical light-series tables.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "@react-three/drei"
  ],
  "registryDependencies": [
    "https://deck-twin-library.demoareeb.workers.dev/r/core.json"
  ],
  "files": [
    {
      "path": "piping/pipe-clamp.tsx",
      "type": "registry:file",
      "target": "components/deck-twin/piping/pipe-clamp.tsx",
      "content": "// Pipe clamps per MSS SP-58: two-bolt (Type 4) and three-bolt (Type 3) hanger clamps on a rod with weldless eye nut, and a riser clamp bearing on steel.\nimport * as THREE from 'three'\nimport type { Kit, MatKey } from '../lib/kit'\nimport { KitMesh, useKit, type GroupProps } from '../lib/mesh'\nimport { member } from '../lib/parts'\nimport { inch, pipeOD, pipeWall } from '../lib/standards'\n\nexport type ClampType = 'two-bolt' | 'three-bolt' | 'riser'\n\nexport interface PipeClampProps {\n  type?: ClampType\n  nps?: number\n  /** hanger rod length from the clamp to the steel above, m (two- and three-bolt) */\n  rod?: number\n  /** draw a length of the pipe for context */\n  pipe?: boolean\n  finish?: 'galvanised' | 'painted'\n}\n\nexport const defaults = { type: 'three-bolt' as ClampType, nps: 6, rod: 0.6, pipe: false, finish: 'galvanised' as 'galvanised' | 'painted' }\n\nexport function PipeClamp({ type = defaults.type, nps = defaults.nps, rod = defaults.rod, pipe = defaults.pipe, finish = defaults.finish, ...group }: PipeClampProps & GroupProps) {\n  const data = useKit((k) => build(k, type, nps, Math.max(0.15, rod), pipe, finish === 'galvanised' ? 'galvanised' : 'structSteel'), [type, nps, rod, pipe, finish])\n  return (\n    <group {...group}>\n      <KitMesh data={data} />\n    </group>\n  )\n}\n\nexport default PipeClamp\n\n// typical strap, bolt and rod sizes by pipe size (vendor tables, light series)\nfunction sizes(nps: number) {\n  const t = nps <= 2 ? 0.005 : nps <= 6 ? 0.008 : nps <= 12 ? 0.01 : 0.0125\n  const w = nps <= 2 ? 0.032 : nps <= 8 ? 0.05 : 0.065\n  const bd = nps <= 2 ? 0.0127 : nps <= 6 ? 0.019 : nps <= 12 ? 0.022 : 0.025\n  const rd = nps <= 2 ? 0.0095 : nps <= 6 ? 0.0159 : nps <= 12 ? 0.0222 : 0.0254\n  return { t, w, bd, rd }\n}\n\n/** One strap half on the +u side: ear up to `top`, half ring round the pipe, ear down to -`bot`. Shape in (u, v), gap between ears 2·g. */\nfunction strapShape(ro: number, t: number, g: number, top: number, bot: number) {\n  const s = new THREE.Shape()\n  const ao = Math.asin(Math.min((g + t) / (ro + t), 0.99))\n  const ai = Math.asin(Math.min(g / ro, 0.99))\n  s.moveTo(g + t, top)\n  s.lineTo(g + t, (ro + t) * Math.cos(ao))\n  s.absarc(0, 0, ro + t, Math.PI / 2 - ao, -Math.PI / 2 + ao, true)\n  s.lineTo(g + t, -bot)\n  s.lineTo(g, -bot)\n  s.lineTo(g, -ro * Math.cos(ai))\n  s.absarc(0, 0, ro, -Math.PI / 2 + ai, Math.PI / 2 - ai, false)\n  s.lineTo(g, top)\n  s.closePath()\n  return s\n}\n\nfunction bolt(k: Kit, mat: MatKey, y: number, x: number, g: number, t: number, d: number, axis: 'z' = 'z') {\n  const L = 2 * (g + t) + d * 2.4\n  k.cyl(mat, d / 2, L, [x, y, 0], axis, 10)\n  k.hex(mat, d * 1.9, d * 0.7, [x, y, -(g + t + d * 0.35)], axis)\n  k.hex(mat, d * 1.9, d * 0.85, [x, y, g + t + d * 0.43], axis)\n}\n\nfunction build(k: Kit, type: ClampType, nps: number, rodL: number, showPipe: boolean, mat: MatKey) {\n  const od = pipeOD(nps)\n  const ro = od / 2 + 0.001\n  const { t, w, bd, rd } = sizes(nps)\n  if (type === 'riser') return riser(k, nps, ro, showPipe, mat)\n  const g = type === 'three-bolt' ? Math.max(bd * 0.75, inch(0.53)) : bd * 0.6\n  // three-bolt: long upper ears with a load bolt and a spacer bolt; two-bolt: short ears, load bolt just above the pipe\n  const top = type === 'three-bolt' ? ro + Math.max(0.07, od * 0.45) : ro + bd * 2.6\n  const bot = ro + bd * 2.4\n  const yLoad = top - bd * 1.4\n  const m = (sx: number) => new THREE.Matrix4().makeBasis(new THREE.Vector3(0, 0, sx), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0)).setPosition(-w / 2, 0, 0)\n  for (const sx of [-1, 1]) k.extrude(mat, strapShape(ro, t, g, top, bot), w, m(sx), 40)\n  bolt(k, mat, yLoad, 0, g, t, bd)\n  bolt(k, mat, -(ro + bd * 1.2), 0, g, t, bd)\n  if (type === 'three-bolt') bolt(k, mat, ro + bd * 1.3, 0, g, t, bd)\n  // weldless eye nut on the load bolt, hanger rod with lock nut, welded lug on a short beam above\n  k.torus(mat, bd * 0.95, bd * 0.38, [0, yLoad, 0], 'z', 8, 20)\n  const yEye = yLoad + bd * 1.3\n  k.box(mat, [rd * 2.4, rd * 2.6, Math.min(2 * g - 0.002, rd * 2)], [0, yEye + rd * 1.3, 0])\n  const yTop = yEye + rd * 2.6 + rodL\n  k.cyl(mat, rd / 2, rodL + 0.04, [0, yEye + rd * 2.6 + (rodL - 0.04) / 2, 0], 'y', 10)\n  k.hex('nut', rd * 1.9, rd * 0.9, [0, yEye + rd * 3.1, 0])\n  k.hex('nut', rd * 1.9, rd * 0.9, [0, yTop - 0.06, 0])\n  k.box(mat, [0.07, 0.07, 0.012], [0, yTop - 0.035, 0])\n  k.cyl(mat, rd * 0.8, 0.014, [0, yTop - 0.045, 0], 'z', 12)\n  member(k, [0, yTop + 0.1, -0.25], [0, yTop + 0.1, 0.25], 'IPE 200', 'structSteel', [0, 1, 0])\n  if (showPipe) k.pipe('pipePaint', [-0.6, 0, 0], [0.6, 0, 0], od, pipeWall(nps, 'STD'))\n}\n\n/** Riser clamp: two bent bars bolted across a vertical pipe, ears bearing on two support beams. */\nfunction riser(k: Kit, nps: number, ro: number, showPipe: boolean, mat: MatKey) {\n  const od = pipeOD(nps)\n  const t = nps <= 4 ? 0.008 : nps <= 8 ? 0.0125 : 0.016\n  const h = nps <= 4 ? 0.05 : 0.065\n  const bd = nps <= 4 ? 0.0127 : nps <= 12 ? 0.016 : 0.02\n  const A = od + inch(9.5)\n  const g = bd * 0.6\n  // bar shape in plan (u = x along the bar, v = z), mirrored for the second bar\n  const s = new THREE.Shape()\n  const ao = Math.acos(Math.min((g + t) / (ro + t), 0.99))\n  const ai = Math.acos(Math.min(g / ro, 0.99))\n  s.moveTo(-A / 2, g)\n  s.lineTo(-ro * Math.sin(ai), g)\n  s.absarc(0, 0, ro, Math.PI / 2 + ai, Math.PI / 2 - ai, true)\n  s.lineTo(A / 2, g)\n  s.lineTo(A / 2, g + t)\n  s.lineTo((ro + t) * Math.sin(ao), g + t)\n  s.absarc(0, 0, ro + t, Math.PI / 2 - ao, Math.PI / 2 + ao, false)\n  s.lineTo(-A / 2, g + t)\n  s.closePath()\n  for (const sz of [-1, 1]) {\n    // shape v maps to ±Z; the extrusion runs through the bar depth h along Y\n    k.extrude(mat, s, h, new THREE.Matrix4().makeBasis(new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, sz), new THREE.Vector3(0, -sz, 0)).setPosition(0, (sz * h) / 2, 0), 40)\n  }\n  for (const sx of [-1, 1]) {\n    const x = sx * (A / 2 - bd * 1.8)\n    k.cyl(mat, bd / 2, 2 * (g + t) + bd * 2.4, [x, 0, 0], 'z', 10)\n    k.hex(mat, bd * 1.9, bd * 0.7, [x, 0, -(g + t + bd * 0.35)], 'z')\n    k.hex(mat, bd * 1.9, bd * 0.85, [x, 0, g + t + bd * 0.43], 'z')\n    // ears bear on short channel supports\n    const xs = sx * (A / 2 - 0.035)\n    member(k, [xs, -h / 2 - 0.05, -0.18], [xs, -h / 2 - 0.05, 0.18], 'UPN 100', 'structSteel', [0, 1, 0])\n  }\n  if (showPipe) k.pipe('pipePaint', [0, -0.7, 0], [0, 0.7, 0], od, pipeWall(nps, 'STD'))\n}\n"
    }
  ],
  "categories": [
    "piping"
  ],
  "meta": {
    "export": "PipeClamp",
    "anchor": "fitting",
    "standards": [
      "MSS SP-58",
      "MSS SP-69",
      "ASME B31.3"
    ]
  }
}