@faicad/sheetmetal 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/allowanceFns.d.ts +42 -0
  2. package/dist/allowanceFns.d.ts.map +1 -0
  3. package/dist/allowanceFns.js +63 -0
  4. package/dist/allowanceFns.js.map +1 -0
  5. package/dist/api.d.ts +312 -0
  6. package/dist/api.d.ts.map +1 -0
  7. package/dist/api.js +367 -0
  8. package/dist/api.js.map +1 -0
  9. package/dist/authorFns.d.ts +118 -0
  10. package/dist/authorFns.d.ts.map +1 -0
  11. package/dist/authorFns.js +562 -0
  12. package/dist/authorFns.js.map +1 -0
  13. package/dist/bendTableFns.d.ts +87 -0
  14. package/dist/bendTableFns.d.ts.map +1 -0
  15. package/dist/bendTableFns.js +345 -0
  16. package/dist/bendTableFns.js.map +1 -0
  17. package/dist/compat.d.ts +90 -0
  18. package/dist/compat.d.ts.map +1 -0
  19. package/dist/compat.js +99 -0
  20. package/dist/compat.js.map +1 -0
  21. package/dist/contourFlangeFns.d.ts +122 -0
  22. package/dist/contourFlangeFns.d.ts.map +1 -0
  23. package/dist/contourFlangeFns.js +403 -0
  24. package/dist/contourFlangeFns.js.map +1 -0
  25. package/dist/cutoutFns.d.ts +95 -0
  26. package/dist/cutoutFns.d.ts.map +1 -0
  27. package/dist/cutoutFns.js +300 -0
  28. package/dist/cutoutFns.js.map +1 -0
  29. package/dist/dxfFns.d.ts +48 -0
  30. package/dist/dxfFns.d.ts.map +1 -0
  31. package/dist/dxfFns.js +236 -0
  32. package/dist/dxfFns.js.map +1 -0
  33. package/dist/facade.d.ts +449 -0
  34. package/dist/facade.d.ts.map +1 -0
  35. package/dist/facade.js +535 -0
  36. package/dist/facade.js.map +1 -0
  37. package/dist/featureTreeFns.d.ts +76 -0
  38. package/dist/featureTreeFns.d.ts.map +1 -0
  39. package/dist/featureTreeFns.js +190 -0
  40. package/dist/featureTreeFns.js.map +1 -0
  41. package/dist/foldFns.d.ts +83 -0
  42. package/dist/foldFns.d.ts.map +1 -0
  43. package/dist/foldFns.js +744 -0
  44. package/dist/foldFns.js.map +1 -0
  45. package/dist/foreignUnfoldFns.d.ts +62 -0
  46. package/dist/foreignUnfoldFns.d.ts.map +1 -0
  47. package/dist/foreignUnfoldFns.js +854 -0
  48. package/dist/foreignUnfoldFns.js.map +1 -0
  49. package/dist/formFns.d.ts +58 -0
  50. package/dist/formFns.d.ts.map +1 -0
  51. package/dist/formFns.js +300 -0
  52. package/dist/formFns.js.map +1 -0
  53. package/dist/hemFns.d.ts +17 -0
  54. package/dist/hemFns.d.ts.map +1 -0
  55. package/dist/hemFns.js +212 -0
  56. package/dist/hemFns.js.map +1 -0
  57. package/dist/index.d.ts +43 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +32 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/internal.d.ts +27 -0
  62. package/dist/internal.d.ts.map +1 -0
  63. package/dist/internal.js +38 -0
  64. package/dist/internal.js.map +1 -0
  65. package/dist/jogFns.d.ts +18 -0
  66. package/dist/jogFns.d.ts.map +1 -0
  67. package/dist/jogFns.js +180 -0
  68. package/dist/jogFns.js.map +1 -0
  69. package/dist/loftedFlangeFns.d.ts +23 -0
  70. package/dist/loftedFlangeFns.d.ts.map +1 -0
  71. package/dist/loftedFlangeFns.js +306 -0
  72. package/dist/loftedFlangeFns.js.map +1 -0
  73. package/dist/materials.d.ts +16 -0
  74. package/dist/materials.d.ts.map +1 -0
  75. package/dist/materials.js +44 -0
  76. package/dist/materials.js.map +1 -0
  77. package/dist/miterFns.d.ts +31 -0
  78. package/dist/miterFns.d.ts.map +1 -0
  79. package/dist/miterFns.js +128 -0
  80. package/dist/miterFns.js.map +1 -0
  81. package/dist/nestFns.d.ts +116 -0
  82. package/dist/nestFns.d.ts.map +1 -0
  83. package/dist/nestFns.js +517 -0
  84. package/dist/nestFns.js.map +1 -0
  85. package/dist/polygonFns.d.ts +144 -0
  86. package/dist/polygonFns.d.ts.map +1 -0
  87. package/dist/polygonFns.js +324 -0
  88. package/dist/polygonFns.js.map +1 -0
  89. package/dist/reliefFns.d.ts +44 -0
  90. package/dist/reliefFns.d.ts.map +1 -0
  91. package/dist/reliefFns.js +367 -0
  92. package/dist/reliefFns.js.map +1 -0
  93. package/dist/reportFns.d.ts +33 -0
  94. package/dist/reportFns.d.ts.map +1 -0
  95. package/dist/reportFns.js +71 -0
  96. package/dist/reportFns.js.map +1 -0
  97. package/dist/tabFns.d.ts +43 -0
  98. package/dist/tabFns.d.ts.map +1 -0
  99. package/dist/tabFns.js +179 -0
  100. package/dist/tabFns.js.map +1 -0
  101. package/dist/types.d.ts +629 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +2 -0
  104. package/dist/types.js.map +1 -0
  105. package/dist/unfoldFns.d.ts +120 -0
  106. package/dist/unfoldFns.d.ts.map +1 -0
  107. package/dist/unfoldFns.js +766 -0
  108. package/dist/unfoldFns.js.map +1 -0
  109. package/dist/validateFns.d.ts +18 -0
  110. package/dist/validateFns.d.ts.map +1 -0
  111. package/dist/validateFns.js +196 -0
  112. package/dist/validateFns.js.map +1 -0
  113. package/package.json +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreignUnfoldFns.js","sourceRoot":"","sources":["../src/foreignUnfoldFns.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Result,\n type Vec3,\n type Solid,\n type Face,\n ok,\n err,\n validationError,\n getFaces,\n getSurfaceType,\n pointOnSurface,\n normalAt,\n faceCenter,\n sharedEdges,\n measureArea,\n getBounds,\n isValid,\n vecAdd,\n vecSub,\n vecScale,\n vecDot,\n vecCross,\n vecLength,\n vecNormalize,\n line,\n wireLoop,\n} from '@faicad/faijs/brepjs-compat';\nimport type {\n UnfoldResult,\n FlatPattern,\n BendReport,\n SheetMetalWarning,\n} from './types.js';\nimport { developedLength } from './allowanceFns.js';\n\n/** Default neutral-axis K-factor for a purely geometric unfold (mid-surface). */\nconst DEFAULT_FOREIGN_K = 0.5;\n/** Relative tolerance for the cylinder-fit residual (fraction of the radius). */\nconst FIT_RESIDUAL_TOL = 1e-2;\n/** Absolute tolerance for matching parallel planes / coincident geometry. */\nconst GEOM_TOL = 1e-4;\n\n/** A fitted cylinder recovered numerically from a face's sampled points + normals. */\nexport interface FittedCylinder {\n /** A point on the axis line. */\n axisOrigin: Vec3;\n /** Unit axis direction. */\n axisDir: Vec3;\n /** Fitted radius (mean distance from the axis line to the sampled points). */\n radius: number;\n /** Angular extent swept by the face about the axis, in radians. */\n angleSpan: number;\n /** Max relative residual of the fit (fraction of radius); small for a true cylinder. */\n residual: number;\n}\n\ninterface Sample {\n point: Vec3;\n normal: Vec3;\n}\n\n/**\n * Sample a regular grid of surface points and their normals across the face's UV\n * bounds. `pointOnSurface` takes normalized (0-1) UV; `normalAt` re-projects the\n * sampled point to UV internally, so the pair holds for true cylinders/planes but\n * is not an exact-UV guarantee for degenerate parameterizations.\n */\nfunction sampleFace(face: Face, n: number): Sample[] {\n const samples: Sample[] = [];\n for (let i = 0; i < n; i += 1) {\n const u = n === 1 ? 0.5 : i / (n - 1);\n for (let j = 0; j < n; j += 1) {\n const v = n === 1 ? 0.5 : j / (n - 1);\n const point = pointOnSurface(face, u, v);\n const normal = normalAt(face, point);\n const len = vecLength(normal);\n if (len < GEOM_TOL) continue;\n samples.push({ point, normal: vecScale(normal, 1 / len) });\n }\n }\n return samples;\n}\n\n/**\n * Numerically fit a cylinder to a face.\n *\n * Every normal of a cylinder is perpendicular to the axis, so the axis direction\n * is the common perpendicular of the sampled normals: it is recovered as the\n * sign-aligned average of the cross products of pairs of non-parallel sampled\n * normals (each such cross product lies along ±axis). The axis LINE and RADIUS\n * are then found by projecting the sample points onto the plane ⟂ axis and\n * fitting a circle (algebraic least squares); the radius is the mean distance\n * from the axis line to the points. The angular span is the extent of the\n * projected points about the fitted centre. Returns `null` when the surface\n * does not fit a cylinder within {@link FIT_RESIDUAL_TOL}.\n *\n * @param face - the B-rep face to fit.\n * @returns the fitted cylinder, or null if the face is not cylindrical.\n */\nexport function fitCylinder(face: Face): FittedCylinder | null {\n const surf = getSurfaceType(face);\n if (!surf.ok || surf.value !== 'CYLINDRE') return null;\n\n const samples = sampleFace(face, 6);\n if (samples.length < 4) return null;\n\n const axisDir = axisFromNormals(samples.map((s) => s.normal));\n if (axisDir === null) return null;\n\n // Project points onto the plane ⟂ axis (in a local 2D basis) and fit a circle.\n const basis = perpBasis(axisDir);\n const pts2: [number, number][] = samples.map((s) => [\n vecDot(s.point, basis.e1),\n vecDot(s.point, basis.e2),\n ]);\n const circle = fitCircle2(pts2);\n if (circle === null) return null;\n\n // Lift the 2D centre back to a 3D point on the axis: use the axial component of\n // an arbitrary sample so the origin sits at a real location on the face.\n const axial = vecDot(samples[0]?.point ?? [0, 0, 0], axisDir);\n const axisOrigin = vecAdd(\n vecAdd(vecScale(basis.e1, circle.cx), vecScale(basis.e2, circle.cy)),\n vecScale(axisDir, axial)\n );\n\n // Radius = mean distance from the axis line to the sampled points; residual is\n // the max relative deviation from that radius (a true cylinder is near zero).\n let sumR = 0;\n for (const s of samples) sumR += distanceToAxis(s.point, axisOrigin, axisDir);\n const radius = sumR / samples.length;\n if (radius < GEOM_TOL) return null;\n let maxDev = 0;\n for (const s of samples) {\n const d = distanceToAxis(s.point, axisOrigin, axisDir);\n maxDev = Math.max(maxDev, Math.abs(d - radius));\n }\n const residual = maxDev / radius;\n if (residual > FIT_RESIDUAL_TOL) return null;\n\n const angleSpan = angularExtent(pts2, circle.cx, circle.cy);\n return { axisOrigin, axisDir, radius, angleSpan, residual };\n}\n\n/**\n * Axis direction as the common perpendicular of the cylinder normals: average the\n * cross products of pairs of sufficiently non-parallel normals, aligning each to a\n * consistent hemisphere before summing so they don't cancel. Returns `null` if no\n * pair is non-parallel (a degenerate/near-planar sample).\n */\nfunction axisFromNormals(normals: Vec3[]): Vec3 | null {\n let acc: Vec3 = [0, 0, 0];\n let ref: Vec3 | null = null;\n for (let i = 0; i < normals.length; i += 1) {\n for (let j = i + 1; j < normals.length; j += 1) {\n const a = normals[i];\n const b = normals[j];\n if (a === undefined || b === undefined) continue;\n const c = vecCross(a, b);\n const len = vecLength(c);\n if (len < 1e-3) continue;\n let unit = vecScale(c, 1 / len);\n if (ref === null) ref = unit;\n else if (vecDot(unit, ref) < 0) unit = vecScale(unit, -1);\n acc = vecAdd(acc, unit);\n }\n }\n if (vecLength(acc) < GEOM_TOL) return null;\n return vecNormalize(acc);\n}\n\n/** An orthonormal pair spanning the plane perpendicular to `axis`. */\nfunction perpBasis(axis: Vec3): { e1: Vec3; e2: Vec3 } {\n const seed: Vec3 = Math.abs(axis[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];\n const e1 = vecNormalize(vecCross(axis, seed));\n const e2 = vecNormalize(vecCross(axis, e1));\n return { e1, e2 };\n}\n\n/** Perpendicular distance from `p` to the line through `o` with unit direction `d`. */\nfunction distanceToAxis(p: Vec3, o: Vec3, d: Vec3): number {\n const w = vecSub(p, o);\n const along = vecDot(w, d);\n const perp = vecSub(w, vecScale(d, along));\n return vecLength(perp);\n}\n\n/** Algebraic (Kåsa) least-squares circle fit to 2D points. */\nfunction fitCircle2(pts: [number, number][]): { cx: number; cy: number; r: number } | null {\n const n = pts.length;\n if (n < 3) return null;\n let sx = 0;\n let sy = 0;\n let sxx = 0;\n let syy = 0;\n let sxy = 0;\n let sxz = 0;\n let syz = 0;\n let sz = 0;\n for (const [x, y] of pts) {\n const z = x * x + y * y;\n sx += x;\n sy += y;\n sxx += x * x;\n syy += y * y;\n sxy += x * y;\n sxz += x * z;\n syz += y * z;\n sz += z;\n }\n // Solve the normal equations for [A, B, C] in A·x + B·y + C = z (= x²+y²).\n const m = [\n [sxx, sxy, sx],\n [sxy, syy, sy],\n [sx, sy, n],\n ];\n const rhs = [sxz, syz, sz];\n const sol = solve3(m, rhs);\n if (sol === null) return null;\n const [a, b, c] = sol;\n const cx = a / 2;\n const cy = b / 2;\n const r2 = c + cx * cx + cy * cy;\n if (r2 <= 0) return null;\n return { cx, cy, r: Math.sqrt(r2) };\n}\n\n/** Solve a 3×3 linear system by Cramer's rule; `null` if (near-)singular. */\nfunction solve3(m: number[][], rhs: number[]): [number, number, number] | null {\n const det = det3(m);\n if (Math.abs(det) < 1e-12) return null;\n const mx = [\n [rhs[0] ?? 0, m[0]?.[1] ?? 0, m[0]?.[2] ?? 0],\n [rhs[1] ?? 0, m[1]?.[1] ?? 0, m[1]?.[2] ?? 0],\n [rhs[2] ?? 0, m[2]?.[1] ?? 0, m[2]?.[2] ?? 0],\n ];\n const my = [\n [m[0]?.[0] ?? 0, rhs[0] ?? 0, m[0]?.[2] ?? 0],\n [m[1]?.[0] ?? 0, rhs[1] ?? 0, m[1]?.[2] ?? 0],\n [m[2]?.[0] ?? 0, rhs[2] ?? 0, m[2]?.[2] ?? 0],\n ];\n const mz = [\n [m[0]?.[0] ?? 0, m[0]?.[1] ?? 0, rhs[0] ?? 0],\n [m[1]?.[0] ?? 0, m[1]?.[1] ?? 0, rhs[1] ?? 0],\n [m[2]?.[0] ?? 0, m[2]?.[1] ?? 0, rhs[2] ?? 0],\n ];\n return [det3(mx) / det, det3(my) / det, det3(mz) / det];\n}\n\nfunction det3(m: number[][]): number {\n const a = m[0] ?? [];\n const b = m[1] ?? [];\n const c = m[2] ?? [];\n return (\n (a[0] ?? 0) * ((b[1] ?? 0) * (c[2] ?? 0) - (b[2] ?? 0) * (c[1] ?? 0)) -\n (a[1] ?? 0) * ((b[0] ?? 0) * (c[2] ?? 0) - (b[2] ?? 0) * (c[0] ?? 0)) +\n (a[2] ?? 0) * ((b[0] ?? 0) * (c[1] ?? 0) - (b[1] ?? 0) * (c[0] ?? 0))\n );\n}\n\n/** Angular extent (radians) spanned by 2D points about a centre. */\nfunction angularExtent(pts: [number, number][], cx: number, cy: number): number {\n const angles = pts.map(([x, y]) => Math.atan2(y - cy, x - cx)).sort((a, b) => a - b);\n if (angles.length < 2) return 0;\n // Largest gap between consecutive sorted angles (wrapping); the span is 2π minus it.\n let maxGap = (angles[0] ?? 0) + 2 * Math.PI - (angles[angles.length - 1] ?? 0);\n for (let i = 1; i < angles.length; i += 1) {\n maxGap = Math.max(maxGap, (angles[i] ?? 0) - (angles[i - 1] ?? 0));\n }\n return 2 * Math.PI - maxGap;\n}\n\n// ---------------------------------------------------------------------------\n// Foreign-solid unfold\n// ---------------------------------------------------------------------------\n\n/** A detected flat panel: a pair of large parallel planar faces a thickness apart. */\ninterface DetectedFlat {\n id: string;\n /** The two parallel planar faces (outer + inner) of the panel. */\n faces: [Face, Face];\n /** Mid-surface plane normal (unit). */\n normal: Vec3;\n /** Centroid of the mid-surface. */\n center: Vec3;\n /** Mid-surface area (mean of the two face areas). */\n area: number;\n}\n\n/** A detected bend: a pair of coaxial inner/outer cylindrical faces. */\ninterface DetectedBend {\n id: string;\n faces: Face[];\n fit: FittedCylinder;\n /** Inner radius (smaller of the fitted pair). */\n innerRadius: number;\n /** Swept angle in degrees. */\n angleDeg: number;\n}\n\n/**\n * Unfold an ARBITRARY imported sheet-metal solid with no feature tree, by\n * detecting its geometry numerically. The supported class is roughly-uniform-\n * thickness solids whose panels are planar and whose bends are cylindrical:\n *\n * 1. Classify faces by surface type — planar faces are panel faces, cylindrical\n * faces are bend faces. Any other surface type is reported `UNSUPPORTED_FACE`.\n * 2. Pair the two large parallel planar faces of each flat panel (a thickness\n * apart); pair the inner/outer cylindrical faces of each bend and fit its\n * axis + inner radius + swept angle via {@link fitCylinder}.\n * 3. Build a bend graph (flats = nodes; a bend connects the two flats it shares\n * edges with), take a spanning tree from a root flat, and turn any non-tree\n * bend into a seam cut (warned).\n * 4. Walk the tree replacing each cylindrical bend region with a developed strip\n * of length `developedLength(angle, thickness, { innerRadius, kFactor })`\n * (`kFactor` defaults to the {@link DEFAULT_FOREIGN_K} mid-surface), laying\n * each flat out into the plane. Bend direction (up/down) is read from the\n * cylinder centre relative to the parent flat.\n *\n * Non-fatal warnings ride inside the Ok payload; the function fails only when the\n * input is not a recognizable sheet-metal solid at all.\n *\n * @param solid - the foreign solid to unfold.\n * @param opts - optional k-factor override.\n * @returns the unfold result, or an error.\n */\nexport function unfoldForeignSolid(\n solid: Solid,\n opts?: { kFactor?: number | undefined }\n): Result<UnfoldResult> {\n if (!isValid(solid)) {\n return err(validationError('INVALID_SOLID', 'foreign solid is not a valid B-rep'));\n }\n const kFactor = opts?.kFactor ?? DEFAULT_FOREIGN_K;\n if (!Number.isFinite(kFactor) || kFactor < 0 || kFactor > 1) {\n return err(validationError('INVALID_K_FACTOR', `kFactor must be in [0, 1], got ${kFactor}`));\n }\n\n const warnings: SheetMetalWarning[] = [];\n const faces = getFaces<'3D'>(solid);\n if (faces.length === 0) {\n return err(validationError('EMPTY_SOLID', 'foreign solid has no faces'));\n }\n\n const planarFaces: Face[] = [];\n const cylFaces: Face[] = [];\n for (const face of faces) {\n const t = getSurfaceType(face);\n if (!t.ok) {\n warnings.push({ code: 'UNSUPPORTED_FACE', message: 'failed to read a face surface type; skipping' });\n continue;\n }\n if (t.value === 'PLANE') planarFaces.push(face);\n else if (t.value === 'CYLINDRE') cylFaces.push(face);\n else {\n warnings.push({\n code: 'UNSUPPORTED_FACE',\n message: `UNSUPPORTED_FACE: surface type '${t.value}' is not a planar panel or cylindrical bend; the unfold ignores it and may be incomplete`,\n });\n }\n }\n\n const thicknessResult = detectThickness(planarFaces);\n if (!thicknessResult.ok) return thicknessResult;\n const thickness = thicknessResult.value;\n\n const flats = pairFlats(planarFaces, thickness, warnings);\n if (flats.length === 0) {\n return err(\n validationError('NO_FLATS', 'no planar panel pairs detected; not a recognizable sheet-metal solid')\n );\n }\n\n const bends = pairBends(cylFaces, warnings);\n\n const graph = buildBendGraph(flats, bends, warnings);\n const layout = layoutForeign(flats, graph, thickness, kFactor, warnings);\n if (!layout.ok) return layout;\n\n return ok({ pattern: layout.value.pattern, report: layout.value.report, warnings });\n}\n\n/**\n * Detect the sheet thickness as the smallest gap between an antiparallel pair of\n * planar faces (the two faces of one flat panel). For a roughly-uniform-thickness\n * part this gap is the same across panels; a spread beyond tolerance warns.\n */\nfunction detectThickness(planar: Face[]): Result<number> {\n const gaps: number[] = [];\n for (let i = 0; i < planar.length; i += 1) {\n for (let j = i + 1; j < planar.length; j += 1) {\n const fa = planar[i];\n const fb = planar[j];\n if (fa === undefined || fb === undefined) continue;\n const na = faceNormal(fa);\n const nb = faceNormal(fb);\n if (vecDot(na, nb) > -0.99) continue; // need antiparallel (opposite faces of a panel)\n const ca = faceCenter(fa);\n const cb = faceCenter(fb);\n const gap = Math.abs(vecDot(vecSub(cb, ca), na));\n if (gap > GEOM_TOL) gaps.push(gap);\n }\n }\n if (gaps.length === 0) {\n return err(validationError('NO_THICKNESS', 'could not detect a sheet thickness (no opposed planar pair)'));\n }\n gaps.sort((a, b) => a - b);\n return ok(gaps[0] as number);\n}\n\n/**\n * Pair planar faces into flat panels: each panel is two parallel faces a\n * `thickness` apart with overlapping footprints. Greedy nearest-match by gap.\n */\nfunction pairFlats(planar: Face[], thickness: number, warnings: SheetMetalWarning[]): DetectedFlat[] {\n const used = new Set<number>();\n const flats: DetectedFlat[] = [];\n for (let i = 0; i < planar.length; i += 1) {\n if (used.has(i)) continue;\n const fa = planar[i];\n if (fa === undefined) continue;\n const na = faceNormal(fa);\n const ca = faceCenter(fa);\n let best = -1;\n let bestErr = Infinity;\n for (let j = 0; j < planar.length; j += 1) {\n if (j === i || used.has(j)) continue;\n const fb = planar[j];\n if (fb === undefined) continue;\n const nb = faceNormal(fb);\n if (vecDot(na, nb) > -0.99) continue;\n const cb = faceCenter(fb);\n const gap = Math.abs(vecDot(vecSub(cb, ca), na));\n const e = Math.abs(gap - thickness);\n if (e < bestErr) {\n bestErr = e;\n best = j;\n }\n }\n if (best < 0 || bestErr > thickness * 0.5 + GEOM_TOL) {\n // No usable antiparallel partner at the sheet thickness. A bend-edge cap or\n // rim band is plate-thin in one in-plane direction (≈ thickness) — expected,\n // skip quietly. A face that is wide in BOTH in-plane directions is a real\n // panel; dropping it means non-uniform thickness or unrecognised topology, so\n // flag it. (Area alone can't tell them apart — a long rim is large by area.)\n const db = getBounds(fa);\n const ext = [db.xMax - db.xMin, db.yMax - db.yMin, db.zMax - db.zMin].sort((p, q) => q - p);\n const inPlaneMin = ext[1] ?? 0;\n if (inPlaneMin > thickness * 4) {\n warnings.push({\n code: 'DETECTION_INCOMPLETE',\n message: `a panel-sized planar face (${(ext[0] ?? 0).toFixed(1)}×${inPlaneMin.toFixed(1)}) has no opposite face at the sheet thickness ${thickness.toFixed(3)}; omitted (possible non-uniform thickness)`,\n });\n }\n continue;\n }\n const fb = planar[best] as Face;\n used.add(i);\n used.add(best);\n const areaA = measureArea(fa);\n const areaB = measureArea(fb);\n let area: number;\n if (areaA.ok && areaB.ok) area = (areaA.value + areaB.value) / 2;\n else if (areaA.ok) area = areaA.value;\n else if (areaB.ok) area = areaB.value;\n else {\n // Neither face area is measurable: fall back to the panel's bounding-box\n // footprint so developedArea isn't silently zero, and flag the estimate.\n const b = getBounds(fa);\n const dx = b.xMax - b.xMin;\n const dy = b.yMax - b.yMin;\n const dz = b.zMax - b.zMin;\n area = Math.max(dx * dy, dy * dz, dx * dz);\n warnings.push({\n code: 'DETECTION_INCOMPLETE',\n message: `flat-${flats.length}: face area unmeasurable; using a bounding-box estimate (${area.toFixed(2)})`,\n });\n }\n flats.push({\n id: `flat-${flats.length}`,\n faces: [fa, fb],\n normal: na,\n center: vecScale(vecAdd(ca, faceCenter(fb)), 0.5),\n area,\n });\n }\n if (flats.length === 0 && planar.length > 0) {\n warnings.push({\n code: 'DETECTION_INCOMPLETE',\n message: 'no parallel planar face pairs matched the detected thickness; the part may have non-uniform thickness',\n });\n }\n return flats;\n}\n\n/**\n * Pair cylindrical faces into bends: each bend has an inner + outer coaxial face\n * (same axis, radii a thickness apart) or, failing a pair, a single fitted face.\n */\nfunction pairBends(cyl: Face[], warnings: SheetMetalWarning[]): DetectedBend[] {\n interface Fitted {\n face: Face;\n fit: FittedCylinder;\n }\n const fitted: Fitted[] = [];\n for (const face of cyl) {\n const fit = fitCylinder(face);\n if (fit === null) {\n warnings.push({\n code: 'UNSUPPORTED_FACE',\n message: 'a cylindrical face did not fit a cylinder within tolerance; the bend is ignored',\n });\n continue;\n }\n fitted.push({ face, fit });\n }\n\n const used = new Set<number>();\n const bends: DetectedBend[] = [];\n for (let i = 0; i < fitted.length; i += 1) {\n if (used.has(i)) continue;\n const a = fitted[i];\n if (a === undefined) continue;\n let mate = -1;\n for (let j = i + 1; j < fitted.length; j += 1) {\n if (used.has(j)) continue;\n const b = fitted[j];\n if (b === undefined) continue;\n if (Math.abs(vecDot(a.fit.axisDir, b.fit.axisDir)) < 0.99) continue;\n if (distanceToAxis(b.fit.axisOrigin, a.fit.axisOrigin, a.fit.axisDir) > GEOM_TOL * 10) continue;\n mate = j;\n break;\n }\n const faces: Face[] = [a.face];\n let innerRadius = a.fit.radius;\n let fit = a.fit;\n if (mate >= 0) {\n const b = fitted[mate] as Fitted;\n faces.push(b.face);\n innerRadius = Math.min(a.fit.radius, b.fit.radius);\n fit = a.fit.radius <= b.fit.radius ? a.fit : b.fit;\n used.add(mate);\n }\n used.add(i);\n bends.push({\n id: `bend-${bends.length}`,\n faces,\n fit,\n innerRadius,\n angleDeg: (fit.angleSpan * 180) / Math.PI,\n });\n }\n return bends;\n}\n\ninterface BendGraphEdge {\n bend: DetectedBend;\n flats: [number, number];\n}\n\ninterface BendGraph {\n edges: BendGraphEdge[];\n}\n\n/**\n * Connect bends to the flats they touch: a bend's cylindrical face shares edges\n * with exactly the two flats tangent to it. A bend that touches fewer than two\n * detected flats is dropped (it is a free rim); more than two is ambiguous and\n * warned. Spanning-tree / seam handling happens in {@link layoutForeign}.\n */\nfunction buildBendGraph(\n flats: DetectedFlat[],\n bends: DetectedBend[],\n warnings: SheetMetalWarning[]\n): BendGraph {\n const edges: BendGraphEdge[] = [];\n for (const bend of bends) {\n const touching: number[] = [];\n for (let fi = 0; fi < flats.length; fi += 1) {\n const flat = flats[fi];\n if (flat === undefined) continue;\n if (bendTouchesFlat(bend, flat)) touching.push(fi);\n }\n if (touching.length < 2) {\n warnings.push({\n code: 'DETECTION_INCOMPLETE',\n message: `bend '${bend.id}' connects ${touching.length} flats (expected 2); ignored`,\n featureId: bend.id,\n });\n continue;\n }\n if (touching.length > 2) {\n warnings.push({\n code: 'DETECTION_INCOMPLETE',\n message: `bend '${bend.id}' touches ${touching.length} flats; using the first two`,\n featureId: bend.id,\n });\n }\n edges.push({ bend, flats: [touching[0] as number, touching[1] as number] });\n }\n return { edges };\n}\n\n/** True if any of the bend's faces shares an edge with either face of the flat. */\nfunction bendTouchesFlat(bend: DetectedBend, flat: DetectedFlat): boolean {\n for (const bf of bend.faces) {\n for (const ff of flat.faces) {\n if (sharedEdges(bf, ff).length > 0) return true;\n }\n }\n return false;\n}\n\ninterface ForeignLayout {\n pattern: FlatPattern;\n report: BendReport;\n}\n\n/**\n * Lay out detected flats into the developed plane. A spanning tree over the bend\n * graph (BFS from flat 0) places each flat as a rectangle and each tree bend as a\n * developed strip; non-tree bends become seam cuts (warned). The developed area\n * is the sum of flat mid-surface areas plus each developed-strip area, computed\n * directly from detected geometry. Bend direction is read from the cylinder\n * centre relative to the parent flat's outward normal.\n */\nfunction layoutForeign(\n flats: DetectedFlat[],\n graph: BendGraph,\n thickness: number,\n kFactor: number,\n warnings: SheetMetalWarning[]\n): Result<ForeignLayout> {\n const adjacency = new Map<number, BendGraphEdge[]>();\n for (let i = 0; i < flats.length; i += 1) adjacency.set(i, []);\n for (const e of graph.edges) {\n adjacency.get(e.flats[0])?.push(e);\n adjacency.get(e.flats[1])?.push(e);\n }\n\n const visited = new Set<number>([0]);\n const treeEdges = new Set<BendGraphEdge>();\n const treeBends: { edge: BendGraphEdge; parent: number; child: number }[] = [];\n const queue: number[] = [0];\n while (queue.length > 0) {\n const cur = queue.shift();\n if (cur === undefined) break;\n for (const e of adjacency.get(cur) ?? []) {\n if (treeEdges.has(e)) continue;\n const next = e.flats[0] === cur ? e.flats[1] : e.flats[0];\n if (visited.has(next)) continue;\n visited.add(next);\n treeEdges.add(e);\n treeBends.push({ edge: e, parent: cur, child: next });\n queue.push(next);\n }\n }\n\n for (const e of graph.edges) {\n if (treeEdges.has(e)) continue;\n warnings.push({\n code: 'SEAM_CUT',\n message: `closed profile: bend '${e.bend.id}' becomes a seam cut`,\n featureId: e.bend.id,\n });\n }\n\n // Each bend's strip width = the bend cylinder's axial extent (its span).\n const bendSpan = new Map<DetectedBend, number>();\n for (const e of graph.edges) bendSpan.set(e.bend, bendAxialExtent(e.bend));\n\n // 2D placement: develop each flat into the plane preserving its real fold\n // direction (perpendicular to the bend axis), so an L-bracket lays out L-shaped,\n // not collapsed into a straight chain. Each placed flat carries a consistent\n // 3D→2D affine map (`m2`): a 3D point on the flat's plane maps to 2D via its\n // in-plane basis, so the bend line, edges and develop-out direction are all\n // derived in 3D then mapped to 2D — keeping signs/edges consistent. The root is\n // laid at the origin; a child is placed past its developed strip, out from the\n // shared bend line. The developed AREA is geometry-exact (Σ flat mid-surface\n // areas + Σ developed-strip areas).\n const placed = new Map<number, PlacedFlat>();\n const root = flats[0] as DetectedFlat;\n const rootAxis = treeBends[0]?.edge.bend.fit.axisDir ?? perpBasis(root.normal).e1;\n const rootBasis = planeBasis(root.normal, rootAxis);\n placed.set(0, {\n flat: root,\n a: rootBasis.along,\n b: rootBasis.out,\n o3: root.center,\n o2: [0, 0],\n a2: [1, 0],\n b2: [0, 1],\n });\n\n let developedArea = 0;\n for (const f of flats) developedArea += f.area;\n\n const bendReportRows: BendReport['bends'] = [];\n const rects: Rect2[] = [];\n const bendLines: FlatPattern['bendLines'] = [];\n rects.push(flatRect2(placed.get(0) as PlacedFlat));\n\n for (const tb of treeBends) {\n const bend = tb.edge.bend;\n const dev = developedLength(bend.angleDeg, thickness, { innerRadius: bend.innerRadius, kFactor }, (w) => warnings.push(w));\n if (!dev.ok) return dev;\n const parentPlace = placed.get(tb.parent);\n const parent = flats[tb.parent] as DetectedFlat;\n const child = flats[tb.child] as DetectedFlat;\n if (parentPlace === undefined) continue;\n const direction = bendDirection(bend, parent);\n const axis = bend.fit.axisDir;\n const span = bendSpan.get(bend) ?? bendAxialExtent(bend);\n\n const placement = placeChildFlat(parentPlace, child, bend, axis, span, dev.value);\n placed.set(tb.child, placement.child);\n\n rects.push(placement.strip);\n rects.push(flatRect2(placement.child));\n bendLines.push({\n id: bend.id,\n line: line([placement.bendA[0], placement.bendA[1], 0], [placement.bendB[0], placement.bendB[1], 0]),\n angleDeg: bend.angleDeg,\n direction,\n inward: [-placement.outDir2[0], -placement.outDir2[1]],\n });\n developedArea += dev.value * span;\n\n bendReportRows.push({\n id: bend.id,\n angleDeg: bend.angleDeg,\n radius: bend.innerRadius,\n allowance: dev.value,\n flatLength: placement.runLen,\n direction,\n });\n }\n\n const bbox = aabb2(rects);\n const outline = buildRectOutline2(bbox);\n const outlineWire = wireLoop(outline);\n if (!outlineWire.ok) return outlineWire;\n\n const pattern: FlatPattern = {\n outline: outlineWire.value,\n bendLines,\n holes: [],\n formCuts: [],\n formMarkers: [],\n formHinges: [],\n loftedDevelopments: [],\n developedArea,\n };\n const report: BendReport = {\n bends: bendReportRows,\n totalFlatSize: [bbox.x1 - bbox.x0, bbox.y1 - bbox.y0],\n };\n return ok({ pattern, report });\n}\n\n/**\n * A placed flat: its detected geometry plus a consistent 3D→2D affine map. A 3D\n * point P on the flat's plane maps to `o2 + (P−o3)·a · a2 + (P−o3)·b · b2`, where\n * (a, b) is the flat's in-plane 3D basis and (a2, b2) the 2D images of those axes.\n */\ninterface PlacedFlat {\n flat: DetectedFlat;\n a: Vec3;\n b: Vec3;\n o3: Vec3;\n o2: [number, number];\n a2: [number, number];\n b2: [number, number];\n}\n\ninterface Rect2 {\n x0: number;\n y0: number;\n x1: number;\n y1: number;\n}\n\n/** Map a 3D in-plane point through a placed flat's affine map. */\nfunction mapPoint(p: PlacedFlat, point: Vec3): [number, number] {\n const da = vecDot(vecSub(point, p.o3), p.a);\n const db = vecDot(vecSub(point, p.o3), p.b);\n return [p.o2[0] + da * p.a2[0] + db * p.b2[0], p.o2[1] + da * p.a2[1] + db * p.b2[1]];\n}\n\n/** 3D in-plane basis of a flat: `along` parallel to `axis`, `out` perpendicular. */\nfunction planeBasis(normal: Vec3, axis: Vec3): { along: Vec3; out: Vec3 } {\n const proj = vecSub(axis, vecScale(normal, vecDot(axis, normal)));\n const along = vecLength(proj) > GEOM_TOL ? vecNormalize(proj) : perpBasis(normal).e1;\n const out = vecNormalize(vecCross(normal, along));\n return { along, out };\n}\n\n/** AABB rectangle of a placed flat in 2D (mapping its mid-surface sample corners). */\nfunction flatRect2(p: PlacedFlat): Rect2 {\n const samples = sampleFace(p.flat.faces[0], 4);\n const pts2 = samples.map((s) => mapPoint(p, s.point));\n return rectFrom(pts2);\n}\n\ninterface ChildPlacement {\n child: PlacedFlat;\n strip: Rect2;\n bendA: [number, number];\n bendB: [number, number];\n outDir2: [number, number];\n runLen: number;\n}\n\n/**\n * Place a child flat past its developed bend strip. The shared bend line is the\n * bend axis projected into the parent's plane; the child develops `out` from it on\n * the side away from the parent. Both the bend line and the develop-out direction\n * are computed in 3D then mapped to 2D through the parent's affine map, so the 2D\n * placement stays consistent with the real geometry regardless of basis sign.\n */\nfunction placeChildFlat(\n parent: PlacedFlat,\n childFlat: DetectedFlat,\n bend: DetectedBend,\n axis: Vec3,\n span: number,\n dev: number\n): ChildPlacement {\n // In-plane bend axis direction (the strip-width direction).\n const axisInPlane = vecNormalize(\n vecSub(axis, vecScale(parent.flat.normal, vecDot(axis, parent.flat.normal)))\n );\n // The shared bend line is where the bend meets the parent: the bend axis line\n // projected onto the parent plane, positioned over the parent/child along-axis\n // overlap (the fit's axial origin is arbitrary, so the line is placed by the\n // parent's footprint, not the fit point). Its mid-point along the axis is the\n // centre of the parent footprint along the axis; it spans `span` (the bend's\n // axial extent).\n const axisLineMid = projectToPlane(bend.fit.axisOrigin, parent.flat.normal, parent.o3);\n const lineMid = parentFootprintCentreAlong(parent, axisInPlane, axisLineMid);\n const toBend = vecSub(axisLineMid, parent.flat.center);\n let outDir3 = vecSub(toBend, vecScale(axisInPlane, vecDot(toBend, axisInPlane)));\n if (vecLength(outDir3) < GEOM_TOL) outDir3 = vecNormalize(vecCross(parent.flat.normal, axisInPlane));\n else outDir3 = vecNormalize(outDir3);\n\n const bend3A = vecSub(lineMid, vecScale(axisInPlane, span / 2));\n const bend3B = vecAdd(lineMid, vecScale(axisInPlane, span / 2));\n const bendA = mapPoint(parent, bend3A);\n const bendB = mapPoint(parent, bend3B);\n\n // 2D develop-out direction = image of `outDir3`; 2D along = image of axisInPlane.\n const outBase2 = mapPoint(parent, vecAdd(lineMid, outDir3));\n const lineMid2 = mapPoint(parent, lineMid);\n const outDir2 = unit2([outBase2[0] - lineMid2[0], outBase2[1] - lineMid2[1]]);\n const along2 = unit2([bendB[0] - bendA[0], bendB[1] - bendA[1]]);\n\n const stripFarA: [number, number] = [bendA[0] + outDir2[0] * dev, bendA[1] + outDir2[1] * dev];\n const childBasis = planeBasis(childFlat.normal, axis);\n const runLen = inPlaneRun(childFlat, childBasis.out);\n\n // The child's 2D frame: origin maps the child corner that develops onto\n // `stripFarA` — the corner at minimum bend-axis position (the bend-line start)\n // and minimum run (nearest the bend). `a2`=along2, `b2`=outDir2. Its 3D basis\n // `a`=axisInPlane (shared bend axis, oriented to match `along2`), `b`=child run\n // direction pointing away from the bend.\n const childRunDir = runDirectionAway(childFlat, childBasis.out, bend.fit.axisOrigin);\n const child: PlacedFlat = {\n flat: childFlat,\n a: axisInPlane,\n b: childRunDir,\n o3: childNearCorner(childFlat, axisInPlane, childRunDir),\n o2: stripFarA,\n a2: along2,\n b2: outDir2,\n };\n const stripFarB: [number, number] = [bendB[0] + outDir2[0] * dev, bendB[1] + outDir2[1] * dev];\n const strip = rectFrom([bendA, bendB, stripFarA, stripFarB]);\n return { child, strip, bendA, bendB, outDir2, runLen };\n}\n\n/** Project a point onto a plane (point `o`, unit normal `n`). */\nfunction projectToPlane(p: Vec3, n: Vec3, o: Vec3): Vec3 {\n return vecSub(p, vecScale(n, vecDot(vecSub(p, o), n)));\n}\n\n/**\n * A point on the parent plane at the centre of the parent's footprint along the\n * bend axis, keeping the bend's out-position (so the bend line sits on the parent's\n * edge but is centred over the parent's along-extent). This pins the bend line to\n * the parent footprint rather than the fit's arbitrary axial origin.\n */\nfunction parentFootprintCentreAlong(parent: PlacedFlat, axisInPlane: Vec3, axisRef: Vec3): Vec3 {\n const samples = sampleFace(parent.flat.faces[0], 4);\n let min = Infinity;\n let max = -Infinity;\n for (const s of samples) {\n const v = vecDot(s.point, axisInPlane);\n min = Math.min(min, v);\n max = Math.max(max, v);\n }\n const mid = (min + max) / 2;\n const refAlong = vecDot(axisRef, axisInPlane);\n return vecAdd(axisRef, vecScale(axisInPlane, mid - refAlong));\n}\n\n/** In-plane run extent of a flat along `out` (perpendicular to the bend axis). */\nfunction inPlaneRun(flat: DetectedFlat, out: Vec3): number {\n const samples = sampleFace(flat.faces[0], 4);\n let min = Infinity;\n let max = -Infinity;\n for (const s of samples) {\n const v = vecDot(s.point, out);\n min = Math.min(min, v);\n max = Math.max(max, v);\n }\n return Number.isFinite(max - min) ? max - min : 0;\n}\n\n/** Run direction of a child flat pointing away from the bend axis. */\nfunction runDirectionAway(flat: DetectedFlat, out: Vec3, axisOrigin: Vec3): Vec3 {\n const toCenter = vecSub(flat.center, axisOrigin);\n return vecDot(toCenter, out) >= 0 ? out : vecScale(out, -1);\n}\n\n/**\n * The child corner that develops onto the strip-far near point: minimum along the\n * (oriented) bend axis and minimum along the run (nearest the bend). Searched over\n * the in-plane bounds so `(P−o3)·a ∈ [0, span]` and `(P−o3)·b ∈ [0, runLen]`.\n */\nfunction childNearCorner(flat: DetectedFlat, axis: Vec3, run: Vec3): Vec3 {\n const samples = sampleFace(flat.faces[0], 4);\n let minAxis = Infinity;\n let minRun = Infinity;\n for (const s of samples) {\n minAxis = Math.min(minAxis, vecDot(s.point, axis));\n minRun = Math.min(minRun, vecDot(s.point, run));\n }\n // Reconstruct the corner in the plane: shift a reference sample to the\n // (minAxis, minRun) coordinates so `(P−corner)·axis ∈ [0, span]` and\n // `(P−corner)·run ∈ [0, runLen]` over the flat.\n const ref = samples[0]?.point ?? flat.center;\n const da = minAxis - vecDot(ref, axis);\n const db = minRun - vecDot(ref, run);\n return vecAdd(vecAdd(ref, vecScale(axis, da)), vecScale(run, db));\n}\n\nfunction unit2(v: [number, number]): [number, number] {\n const len = Math.hypot(v[0], v[1]);\n return len < 1e-12 ? [1, 0] : [v[0] / len, v[1] / len];\n}\n\nfunction rectFrom(pts: [number, number][]): Rect2 {\n let x0 = Infinity;\n let y0 = Infinity;\n let x1 = -Infinity;\n let y1 = -Infinity;\n for (const [x, y] of pts) {\n x0 = Math.min(x0, x);\n y0 = Math.min(y0, y);\n x1 = Math.max(x1, x);\n y1 = Math.max(y1, y);\n }\n return { x0, y0, x1, y1 };\n}\n\nfunction aabb2(rects: Rect2[]): Rect2 {\n let x0 = Infinity;\n let y0 = Infinity;\n let x1 = -Infinity;\n let y1 = -Infinity;\n for (const r of rects) {\n x0 = Math.min(x0, r.x0);\n y0 = Math.min(y0, r.y0);\n x1 = Math.max(x1, r.x1);\n y1 = Math.max(y1, r.y1);\n }\n if (!Number.isFinite(x0)) return { x0: 0, y0: 0, x1: 0, y1: 0 };\n return { x0, y0, x1, y1 };\n}\n\nfunction buildRectOutline2(b: Rect2): ReturnType<typeof line>[] {\n return [\n line([b.x0, b.y0, 0], [b.x1, b.y0, 0]),\n line([b.x1, b.y0, 0], [b.x1, b.y1, 0]),\n line([b.x1, b.y1, 0], [b.x0, b.y1, 0]),\n line([b.x0, b.y1, 0], [b.x0, b.y0, 0]),\n ];\n}\n\n/** Axial extent of a bend (its cylindrical face span along the fitted axis). */\nfunction bendAxialExtent(bend: DetectedBend): number {\n const face = bend.faces[0];\n if (face === undefined) return 0;\n const axis = bend.fit.axisDir;\n const samples = sampleFace(face, 6);\n let min = Infinity;\n let max = -Infinity;\n for (const s of samples) {\n const a = vecDot(s.point, axis);\n min = Math.min(min, a);\n max = Math.max(max, a);\n }\n return Number.isFinite(max - min) ? max - min : 0;\n}\n\n/**\n * Bend direction: 'up' if the cylinder axis sits on the +normal side of the\n * parent flat's mid-surface, 'down' otherwise.\n */\nfunction bendDirection(bend: DetectedBend, parent: DetectedFlat): 'up' | 'down' {\n const toAxis = vecSub(bend.fit.axisOrigin, parent.center);\n return vecDot(toAxis, parent.normal) >= 0 ? 'up' : 'down';\n}\n\nfunction faceNormal(face: Face): Vec3 {\n return vecNormalize(normalAt(face, faceCenter(face)));\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { type Result } from '@faicad/faijs/brepjs-compat';
2
+ import type { FormSpec, SheetMetalPart } from './types.js';
3
+ /**
4
+ * Add a form feature (louver or emboss/dimple) to a named flat region.
5
+ *
6
+ * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming
7
+ * impractical):
8
+ * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and
9
+ * the formed flap is represented as a thin box hinged on one side and tilted up to
10
+ * `height`, fused so it stays connected to the body at the hinge. True forming
11
+ * keeps the flap continuous with the parent and bends it; the cut+tilted-flap
12
+ * representation captures the vent geometry while keeping a single valid solid.
13
+ * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).
14
+ * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by
15
+ * `height`, never through). A true spherical/conical form is approximated by the
16
+ * flat-topped round so the result stays a valid single body.
17
+ *
18
+ * FLAT PATTERN (the fabrication-critical representation): the louver emits its
19
+ * footprint as an OPEN three-side cut path (the fabricator cuts three of its four
20
+ * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint
21
+ * circle as a marker. Forming removes no net material, so the developed outline and
22
+ * area are unchanged. Guards a valid, single-bodied solid.
23
+ *
24
+ * @param part - the sheet metal part to form.
25
+ * @param spec - the form specification.
26
+ * @returns the updated part with the form feature recorded, or an error.
27
+ */
28
+ export declare function addForm(part: SheetMetalPart, spec: FormSpec): Result<SheetMetalPart>;
29
+ /**
30
+ * A louver vent on a region; see {@link addForm}.
31
+ * @param part - the sheet metal part to form.
32
+ * @param opts - louver geometry (region, x, y, length, width, height, direction?).
33
+ * @returns the updated part with the louver feature recorded, or an error.
34
+ */
35
+ export declare function louver(part: SheetMetalPart, opts: {
36
+ region: string;
37
+ x: number;
38
+ y: number;
39
+ length: number;
40
+ width: number;
41
+ height: number;
42
+ direction?: 'up' | 'down';
43
+ }): Result<SheetMetalPart>;
44
+ /**
45
+ * An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}.
46
+ * @param part - the sheet metal part to form.
47
+ * @param opts - emboss geometry (region, x, y, diameter, height, direction?).
48
+ * @returns the updated part with the emboss feature recorded, or an error.
49
+ */
50
+ export declare function emboss(part: SheetMetalPart, opts: {
51
+ region: string;
52
+ x: number;
53
+ y: number;
54
+ diameter: number;
55
+ height: number;
56
+ kind: 'dimple' | 'emboss';
57
+ }): Result<SheetMetalPart>;
58
+ //# sourceMappingURL=formFns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formFns.d.ts","sourceRoot":"","sources":["../src/formFns.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAuBZ,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAe,cAAc,EAAE,MAAM,YAAY,CAAC;AAYxE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,CAapF;AA4JD;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE;IACJ,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC3B,GACA,MAAM,CAAC,cAAc,CAAC,CAExB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAAE,GAC1G,MAAM,CAAC,cAAc,CAAC,CAUxB"}
@@ -0,0 +1,300 @@
1
+ import { ok, err, validationError, box, cylinder, line, wireLoop, face, extrude, cut, fuse, rotate, translate, getSolids, isValid, isPlanarWire, vecAdd, vecScale, vecCross, vecNormalize, } from '@faicad/faijs/brepjs-compat';
2
+ import { normalizeSolid } from './internal.js';
3
+ import { regionFrames, regionDevExtent, mapToFrame2 } from './cutoutFns.js';
4
+ const EPS = 1e-6;
5
+ const HAIR = 0.05;
6
+ const CIRCLE_SEGMENTS = 48;
7
+ /**
8
+ * Add a form feature (louver or emboss/dimple) to a named flat region.
9
+ *
10
+ * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming
11
+ * impractical):
12
+ * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and
13
+ * the formed flap is represented as a thin box hinged on one side and tilted up to
14
+ * `height`, fused so it stays connected to the body at the hinge. True forming
15
+ * keeps the flap continuous with the parent and bends it; the cut+tilted-flap
16
+ * representation captures the vent geometry while keeping a single valid solid.
17
+ * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).
18
+ * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by
19
+ * `height`, never through). A true spherical/conical form is approximated by the
20
+ * flat-topped round so the result stays a valid single body.
21
+ *
22
+ * FLAT PATTERN (the fabrication-critical representation): the louver emits its
23
+ * footprint as an OPEN three-side cut path (the fabricator cuts three of its four
24
+ * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint
25
+ * circle as a marker. Forming removes no net material, so the developed outline and
26
+ * area are unchanged. Guards a valid, single-bodied solid.
27
+ *
28
+ * @param part - the sheet metal part to form.
29
+ * @param spec - the form specification.
30
+ * @returns the updated part with the form feature recorded, or an error.
31
+ */
32
+ export function addForm(part, spec) {
33
+ const solid = part.solid;
34
+ if (solid === undefined) {
35
+ return err(validationError('NO_SOLID', 'addForm: part has no folded solid to form'));
36
+ }
37
+ const framesResult = regionFrames(part, spec.region);
38
+ if (!framesResult.ok)
39
+ return framesResult;
40
+ const { regionId, world, dev } = framesResult.value;
41
+ const ext = regionDevExtent(part, regionId, world);
42
+ if (spec.kind === 'louver')
43
+ return louverForm(part, solid, spec, regionId, world, dev, ext);
44
+ return embossForm(part, solid, spec, regionId, world, dev, ext);
45
+ }
46
+ /** Louver vent: cut the opening, fuse the tilted flap, emit the U-cut + hinge in 2D. */
47
+ function louverForm(part, partSolid, spec, regionId, world, dev, ext) {
48
+ if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {
49
+ return err(validationError('INVALID_FORM', 'louver length/width must be positive'));
50
+ }
51
+ if (!Number.isFinite(spec.height) || spec.height <= 0) {
52
+ return err(validationError('INVALID_FORM', `louver height must be positive, got ${spec.height}`));
53
+ }
54
+ // Louver footprint in region-local coords, centred at (x, y): `length` along u,
55
+ // `width` along v. The hinge is the −v edge of the footprint.
56
+ const hl = spec.length / 2;
57
+ const hw = spec.width / 2;
58
+ const x0 = spec.x - hl;
59
+ const x1 = spec.x + hl;
60
+ const y0 = spec.y - hw;
61
+ const y1 = spec.y + hw;
62
+ if (x0 < -EPS || x1 > ext.uMax + EPS || y0 < -EPS || y1 > ext.vMax + EPS) {
63
+ return err(validationError('FORM_OUT_OF_BOUNDS', `louver at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`));
64
+ }
65
+ const dir = spec.direction ?? 'up';
66
+ const sign = dir === 'up' ? 1 : -1;
67
+ // Cut the vent opening fully through the sheet.
68
+ const openingLocal = [
69
+ [x0, y0],
70
+ [x1, y0],
71
+ [x1, y1],
72
+ [x0, y1],
73
+ ];
74
+ const opening = buildThroughTool(openingLocal, world, part.thickness);
75
+ if (!opening.ok)
76
+ return opening;
77
+ const cutResult = cut(partSolid, opening.value);
78
+ if (!cutResult.ok)
79
+ return cutResult;
80
+ let solid = normalizeSolid(cutResult.value);
81
+ // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to
82
+ // `height` on the formed face. Built in region-local space then placed via the
83
+ // world frame; overlaps the hinge edge so it fuses into one body.
84
+ const flap = buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);
85
+ if (!flap.ok)
86
+ return flap;
87
+ const fused = fuse(solid, flap.value);
88
+ if (!fused.ok)
89
+ return fused;
90
+ solid = normalizeSolid(fused.value);
91
+ if (!isValid(solid) || getSolids(solid).length > 1) {
92
+ return err(validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`));
93
+ }
94
+ // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually
95
+ // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.
96
+ // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —
97
+ // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would
98
+ // tell the fabricator to cut all four sides and drop the flap out entirely.
99
+ // Developed outline/area unchanged (forming is net-material-neutral).
100
+ const cutPath = [
101
+ mapToFrame2([x1, y0], dev),
102
+ mapToFrame2([x1, y1], dev),
103
+ mapToFrame2([x0, y1], dev),
104
+ mapToFrame2([x0, y0], dev),
105
+ ];
106
+ const hingeA = mapToFrame2([x0, y0], dev);
107
+ const hingeB = mapToFrame2([x1, y0], dev);
108
+ const feature = {
109
+ spec,
110
+ region: regionId,
111
+ cuts: [cutPath],
112
+ markers: [],
113
+ hinge: [hingeA, hingeB],
114
+ };
115
+ return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });
116
+ }
117
+ /** Emboss/dimple: fuse a raised cylinder or cut a shallow recess; emit a 2D footprint. */
118
+ function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
119
+ if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {
120
+ return err(validationError('INVALID_FORM', `emboss diameter must be positive, got ${spec.diameter}`));
121
+ }
122
+ if (!Number.isFinite(spec.height) || spec.height <= 0) {
123
+ return err(validationError('INVALID_FORM', `emboss height must be positive, got ${spec.height}`));
124
+ }
125
+ const r = spec.diameter / 2;
126
+ if (spec.x - r < -EPS || spec.x + r > ext.uMax + EPS || spec.y - r < -EPS || spec.y + r > ext.vMax + EPS) {
127
+ return err(validationError('FORM_OUT_OF_BOUNDS', `emboss at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`));
128
+ }
129
+ if (spec.form === 'dimple' && spec.height >= part.thickness - EPS) {
130
+ return err(validationError('INVALID_FORM', `dimple depth ${spec.height} must be less than thickness ${part.thickness}`));
131
+ }
132
+ // Centre of the form on the formed (+n) face, in world space.
133
+ const faceCentre = vecAdd(vecAdd(vecAdd(world.origin, vecScale(world.u, spec.x)), vecScale(world.v, spec.y)), vecScale(world.n, part.thickness));
134
+ let solid;
135
+ if (spec.form === 'emboss') {
136
+ // Raised cylinder: base sunk a hair into the sheet for a robust fuse, extending
137
+ // +n by `height`.
138
+ const base = vecAdd(faceCentre, vecScale(world.n, -HAIR));
139
+ const bump = cylinder(r, spec.height + HAIR, { at: base, axis: world.n });
140
+ const fused = fuse(partSolid, bump);
141
+ if (!fused.ok)
142
+ return fused;
143
+ solid = normalizeSolid(fused.value);
144
+ }
145
+ else {
146
+ // Recess: cut a shallow cylinder down from the formed face by `height` (+HAIR so
147
+ // the tool pokes cleanly through the top surface).
148
+ const top = vecAdd(faceCentre, vecScale(world.n, HAIR));
149
+ const tool = cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });
150
+ const cutResult = cut(partSolid, tool);
151
+ if (!cutResult.ok)
152
+ return cutResult;
153
+ solid = normalizeSolid(cutResult.value);
154
+ }
155
+ if (!isValid(solid) || getSolids(solid).length > 1) {
156
+ return err(validationError('FORM_INVALID_SOLID', `addForm: ${spec.form} on region '${spec.region}' did not form a single valid body`));
157
+ }
158
+ const marker = circleLocal([spec.x, spec.y], r).map((p) => mapToFrame2(p, dev));
159
+ const feature = {
160
+ spec,
161
+ region: regionId,
162
+ cuts: [],
163
+ markers: [marker],
164
+ };
165
+ return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });
166
+ }
167
+ /**
168
+ * A louver vent on a region; see {@link addForm}.
169
+ * @param part - the sheet metal part to form.
170
+ * @param opts - louver geometry (region, x, y, length, width, height, direction?).
171
+ * @returns the updated part with the louver feature recorded, or an error.
172
+ */
173
+ export function louver(part, opts) {
174
+ return addForm(part, { kind: 'louver', ...opts });
175
+ }
176
+ /**
177
+ * An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}.
178
+ * @param part - the sheet metal part to form.
179
+ * @param opts - emboss geometry (region, x, y, diameter, height, direction?).
180
+ * @returns the updated part with the emboss feature recorded, or an error.
181
+ */
182
+ export function emboss(part, opts) {
183
+ return addForm(part, {
184
+ kind: 'emboss',
185
+ region: opts.region,
186
+ x: opts.x,
187
+ y: opts.y,
188
+ diameter: opts.diameter,
189
+ height: opts.height,
190
+ form: opts.kind,
191
+ });
192
+ }
193
+ /** Extrude a region-local loop fully through the sheet (a through-cut tool). */
194
+ function buildThroughTool(local, f, thickness) {
195
+ const base = vecAdd(f.origin, vecScale(f.n, -HAIR));
196
+ const worldPts = local.map(([x, y]) => vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y)));
197
+ const edges = [];
198
+ for (let i = 0; i < worldPts.length; i += 1) {
199
+ const a = worldPts[i];
200
+ const b = worldPts[(i + 1) % worldPts.length];
201
+ if (a === undefined || b === undefined) {
202
+ return err(validationError('FORM_TOOL_FAILED', 'failed to index form loop points'));
203
+ }
204
+ edges.push(line(a, b));
205
+ }
206
+ const wire = wireLoop(edges);
207
+ if (!wire.ok)
208
+ return wire;
209
+ if (!isPlanarWire(wire.value)) {
210
+ return err(validationError('FORM_TOOL_FAILED', 'form profile wire is not planar'));
211
+ }
212
+ const profile = face(wire.value);
213
+ if (!profile.ok)
214
+ return profile;
215
+ const depth = thickness + 2 * HAIR;
216
+ return extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth]);
217
+ }
218
+ /**
219
+ * Tilted louver flap, hinged on the `y0` (−v) edge of the opening: a thin plate
220
+ * spanning `[x0,x1]` along u and `width` along v, tilted up about the hinge so its
221
+ * far edge rises by `height` on the formed face (`sign` = +1 up / −1 down). Built in
222
+ * a canonical local frame (hinge along +X at the origin, depth +Y, thickness +Z),
223
+ * tilted about +X, then mapped onto the world frame's u/v/n.
224
+ */
225
+ function buildLouverFlap(f, x0, x1, y0, width, height, thickness, sign) {
226
+ const plateThk = Math.max(thickness, EPS);
227
+ const len = x1 - x0;
228
+ // Tilt angle so the far edge (depth `width`) rises by `height`.
229
+ const tiltDeg = (Math.atan2(height, width) * 180) / Math.PI;
230
+ // Canonical flap: hinge along +X over [0,len], depth +Y over [0,width], thickness
231
+ // sunk a hair below 0 so it overlaps the sheet at the hinge for a robust fuse.
232
+ let flap = box(len, width, plateThk);
233
+ flap = translate(flap, [0, 0, -plateThk + HAIR]);
234
+ flap = rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] });
235
+ // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner
236
+ // on the formed face.
237
+ const u = vecNormalize(f.u);
238
+ const n = vecNormalize(f.n);
239
+ const v = vecNormalize(vecCross(n, u));
240
+ const hingeCorner = vecAdd(vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)), vecScale(f.n, thickness));
241
+ const placed = mapLocalToWorld(flap, u, v, n, hingeCorner);
242
+ return ok(placed);
243
+ }
244
+ /** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */
245
+ function mapLocalToWorld(s, uT, vT, nT, origin) {
246
+ const m = [uT[0], vT[0], nT[0], uT[1], vT[1], nT[1], uT[2], vT[2], nT[2]];
247
+ const aa = matrixToAxisAngle(m);
248
+ const rotated = aa.angleDeg === 0 ? s : rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });
249
+ return translate(rotated, origin);
250
+ }
251
+ /** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */
252
+ function matrixToAxisAngle(m) {
253
+ const m00 = m[0] ?? 0;
254
+ const m01 = m[1] ?? 0;
255
+ const m02 = m[2] ?? 0;
256
+ const m10 = m[3] ?? 0;
257
+ const m11 = m[4] ?? 0;
258
+ const m12 = m[5] ?? 0;
259
+ const m20 = m[6] ?? 0;
260
+ const m21 = m[7] ?? 0;
261
+ const m22 = m[8] ?? 0;
262
+ const trace = m00 + m11 + m22;
263
+ const cos = Math.max(-1, Math.min(1, (trace - 1) / 2));
264
+ const angle = Math.acos(cos);
265
+ const angleDeg = (angle * 180) / Math.PI;
266
+ if (angle < 1e-9)
267
+ return { axis: [0, 0, 1], angleDeg: 0 };
268
+ if (Math.PI - angle < 1e-6) {
269
+ const xx = (m00 + 1) / 2;
270
+ const yy = (m11 + 1) / 2;
271
+ const zz = (m22 + 1) / 2;
272
+ let axis;
273
+ if (xx >= yy && xx >= zz) {
274
+ const x = Math.sqrt(Math.max(xx, 0));
275
+ axis = [x, (m01 + m10) / (4 * x), (m02 + m20) / (4 * x)];
276
+ }
277
+ else if (yy >= zz) {
278
+ const y = Math.sqrt(Math.max(yy, 0));
279
+ axis = [(m01 + m10) / (4 * y), y, (m12 + m21) / (4 * y)];
280
+ }
281
+ else {
282
+ const z = Math.sqrt(Math.max(zz, 0));
283
+ axis = [(m02 + m20) / (4 * z), (m12 + m21) / (4 * z), z];
284
+ }
285
+ return { axis: vecNormalize(axis), angleDeg: 180 };
286
+ }
287
+ const denom = 2 * Math.sin(angle);
288
+ const axis = [(m21 - m12) / denom, (m02 - m20) / denom, (m10 - m01) / denom];
289
+ return { axis: vecNormalize(axis), angleDeg };
290
+ }
291
+ /** Region-local circle loop (CCW) centred at `c` of radius `r`. */
292
+ function circleLocal(c, r) {
293
+ const pts = [];
294
+ for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {
295
+ const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;
296
+ pts.push([c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)]);
297
+ }
298
+ return pts;
299
+ }
300
+ //# sourceMappingURL=formFns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formFns.js","sourceRoot":"","sources":["../src/formFns.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Result,\n type Vec3,\n type Solid,\n ok,\n err,\n validationError,\n box,\n cylinder,\n line,\n wireLoop,\n face,\n extrude,\n cut,\n fuse,\n rotate,\n translate,\n getSolids,\n isValid,\n isPlanarWire,\n vecAdd,\n vecScale,\n vecCross,\n vecNormalize,\n} from '@faicad/faijs/brepjs-compat';\nimport type { FormSpec, FormFeature, SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\nimport type { FlatFrame } from './authorFns.js';\nimport type { Frame2 } from './unfoldFns.js';\nimport { regionFrames, regionDevExtent, mapToFrame2 } from './cutoutFns.js';\n\nconst EPS = 1e-6;\nconst HAIR = 0.05;\nconst CIRCLE_SEGMENTS = 48;\n\ntype Pt2 = [number, number];\n\n/**\n * Add a form feature (louver or emboss/dimple) to a named flat region.\n *\n * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming\n * impractical):\n * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and\n * the formed flap is represented as a thin box hinged on one side and tilted up to\n * `height`, fused so it stays connected to the body at the hinge. True forming\n * keeps the flap continuous with the parent and bends it; the cut+tilted-flap\n * representation captures the vent geometry while keeping a single valid solid.\n * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).\n * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by\n * `height`, never through). A true spherical/conical form is approximated by the\n * flat-topped round so the result stays a valid single body.\n *\n * FLAT PATTERN (the fabrication-critical representation): the louver emits its\n * footprint as an OPEN three-side cut path (the fabricator cuts three of its four\n * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint\n * circle as a marker. Forming removes no net material, so the developed outline and\n * area are unchanged. Guards a valid, single-bodied solid.\n *\n * @param part - the sheet metal part to form.\n * @param spec - the form specification.\n * @returns the updated part with the form feature recorded, or an error.\n */\nexport function addForm(part: SheetMetalPart, spec: FormSpec): Result<SheetMetalPart> {\n const solid = part.solid;\n if (solid === undefined) {\n return err(validationError('NO_SOLID', 'addForm: part has no folded solid to form'));\n }\n\n const framesResult = regionFrames(part, spec.region);\n if (!framesResult.ok) return framesResult;\n const { regionId, world, dev } = framesResult.value;\n const ext = regionDevExtent(part, regionId, world);\n\n if (spec.kind === 'louver') return louverForm(part, solid, spec, regionId, world, dev, ext);\n return embossForm(part, solid, spec, regionId, world, dev, ext);\n}\n\n/** Louver vent: cut the opening, fuse the tilted flap, emit the U-cut + hinge in 2D. */\nfunction louverForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'louver' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Result<SheetMetalPart> {\n if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {\n return err(validationError('INVALID_FORM', 'louver length/width must be positive'));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `louver height must be positive, got ${spec.height}`));\n }\n // Louver footprint in region-local coords, centred at (x, y): `length` along u,\n // `width` along v. The hinge is the −v edge of the footprint.\n const hl = spec.length / 2;\n const hw = spec.width / 2;\n const x0 = spec.x - hl;\n const x1 = spec.x + hl;\n const y0 = spec.y - hw;\n const y1 = spec.y + hw;\n if (x0 < -EPS || x1 > ext.uMax + EPS || y0 < -EPS || y1 > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `louver at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n\n const dir = spec.direction ?? 'up';\n const sign = dir === 'up' ? 1 : -1;\n\n // Cut the vent opening fully through the sheet.\n const openingLocal: Pt2[] = [\n [x0, y0],\n [x1, y0],\n [x1, y1],\n [x0, y1],\n ];\n const opening = buildThroughTool(openingLocal, world, part.thickness);\n if (!opening.ok) return opening;\n const cutResult = cut(partSolid, opening.value);\n if (!cutResult.ok) return cutResult;\n let solid = normalizeSolid(cutResult.value);\n\n // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to\n // `height` on the formed face. Built in region-local space then placed via the\n // world frame; overlaps the hinge edge so it fuses into one body.\n const flap = buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);\n if (!flap.ok) return flap;\n const fused = fuse(solid, flap.value);\n if (!fused.ok) return fused;\n solid = normalizeSolid(fused.value);\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually\n // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.\n // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —\n // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would\n // tell the fabricator to cut all four sides and drop the flap out entirely.\n // Developed outline/area unchanged (forming is net-material-neutral).\n const cutPath: Pt2[] = [\n mapToFrame2([x1, y0], dev),\n mapToFrame2([x1, y1], dev),\n mapToFrame2([x0, y1], dev),\n mapToFrame2([x0, y0], dev),\n ];\n const hingeA = mapToFrame2([x0, y0], dev);\n const hingeB = mapToFrame2([x1, y0], dev);\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [cutPath],\n markers: [],\n hinge: [hingeA, hingeB],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n}\n\n/** Emboss/dimple: fuse a raised cylinder or cut a shallow recess; emit a 2D footprint. */\nfunction embossForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'emboss' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Result<SheetMetalPart> {\n if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {\n return err(validationError('INVALID_FORM', `emboss diameter must be positive, got ${spec.diameter}`));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `emboss height must be positive, got ${spec.height}`));\n }\n const r = spec.diameter / 2;\n if (spec.x - r < -EPS || spec.x + r > ext.uMax + EPS || spec.y - r < -EPS || spec.y + r > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `emboss at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n if (spec.form === 'dimple' && spec.height >= part.thickness - EPS) {\n return err(\n validationError('INVALID_FORM', `dimple depth ${spec.height} must be less than thickness ${part.thickness}`)\n );\n }\n\n // Centre of the form on the formed (+n) face, in world space.\n const faceCentre = vecAdd(\n vecAdd(vecAdd(world.origin, vecScale(world.u, spec.x)), vecScale(world.v, spec.y)),\n vecScale(world.n, part.thickness)\n );\n\n let solid: Solid;\n if (spec.form === 'emboss') {\n // Raised cylinder: base sunk a hair into the sheet for a robust fuse, extending\n // +n by `height`.\n const base = vecAdd(faceCentre, vecScale(world.n, -HAIR));\n const bump = cylinder(r, spec.height + HAIR, { at: base, axis: world.n });\n const fused = fuse(partSolid, bump);\n if (!fused.ok) return fused;\n solid = normalizeSolid(fused.value);\n } else {\n // Recess: cut a shallow cylinder down from the formed face by `height` (+HAIR so\n // the tool pokes cleanly through the top surface).\n const top = vecAdd(faceCentre, vecScale(world.n, HAIR));\n const tool = cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });\n const cutResult = cut(partSolid, tool);\n if (!cutResult.ok) return cutResult;\n solid = normalizeSolid(cutResult.value);\n }\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: ${spec.form} on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n const marker = circleLocal([spec.x, spec.y], r).map((p) => mapToFrame2(p, dev));\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [],\n markers: [marker],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n}\n\n/**\n * A louver vent on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - louver geometry (region, x, y, length, width, height, direction?).\n * @returns the updated part with the louver feature recorded, or an error.\n */\nexport function louver(\n part: SheetMetalPart,\n opts: {\n region: string;\n x: number;\n y: number;\n length: number;\n width: number;\n height: number;\n direction?: 'up' | 'down';\n }\n): Result<SheetMetalPart> {\n return addForm(part, { kind: 'louver', ...opts });\n}\n\n/**\n * An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - emboss geometry (region, x, y, diameter, height, direction?).\n * @returns the updated part with the emboss feature recorded, or an error.\n */\nexport function emboss(\n part: SheetMetalPart,\n opts: { region: string; x: number; y: number; diameter: number; height: number; kind: 'dimple' | 'emboss' }\n): Result<SheetMetalPart> {\n return addForm(part, {\n kind: 'emboss',\n region: opts.region,\n x: opts.x,\n y: opts.y,\n diameter: opts.diameter,\n height: opts.height,\n form: opts.kind,\n });\n}\n\n/** Extrude a region-local loop fully through the sheet (a through-cut tool). */\nfunction buildThroughTool(local: Pt2[], f: FlatFrame, thickness: number): Result<Solid> {\n const base = vecAdd(f.origin, vecScale(f.n, -HAIR));\n const worldPts: Vec3[] = local.map(([x, y]) =>\n vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y))\n );\n const edges = [];\n for (let i = 0; i < worldPts.length; i += 1) {\n const a = worldPts[i];\n const b = worldPts[(i + 1) % worldPts.length];\n if (a === undefined || b === undefined) {\n return err(validationError('FORM_TOOL_FAILED', 'failed to index form loop points'));\n }\n edges.push(line(a, b));\n }\n const wire = wireLoop(edges);\n if (!wire.ok) return wire;\n if (!isPlanarWire(wire.value)) {\n return err(validationError('FORM_TOOL_FAILED', 'form profile wire is not planar'));\n }\n const profile = face(wire.value);\n if (!profile.ok) return profile;\n const depth = thickness + 2 * HAIR;\n return extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth]);\n}\n\n/**\n * Tilted louver flap, hinged on the `y0` (−v) edge of the opening: a thin plate\n * spanning `[x0,x1]` along u and `width` along v, tilted up about the hinge so its\n * far edge rises by `height` on the formed face (`sign` = +1 up / −1 down). Built in\n * a canonical local frame (hinge along +X at the origin, depth +Y, thickness +Z),\n * tilted about +X, then mapped onto the world frame's u/v/n.\n */\nfunction buildLouverFlap(\n f: FlatFrame,\n x0: number,\n x1: number,\n y0: number,\n width: number,\n height: number,\n thickness: number,\n sign: number\n): Result<Solid> {\n const plateThk = Math.max(thickness, EPS);\n const len = x1 - x0;\n // Tilt angle so the far edge (depth `width`) rises by `height`.\n const tiltDeg = (Math.atan2(height, width) * 180) / Math.PI;\n\n // Canonical flap: hinge along +X over [0,len], depth +Y over [0,width], thickness\n // sunk a hair below 0 so it overlaps the sheet at the hinge for a robust fuse.\n let flap: Solid = box(len, width, plateThk);\n flap = translate(flap, [0, 0, -plateThk + HAIR]);\n flap = rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] });\n\n // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner\n // on the formed face.\n const u = vecNormalize(f.u);\n const n = vecNormalize(f.n);\n const v = vecNormalize(vecCross(n, u));\n const hingeCorner = vecAdd(\n vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)),\n vecScale(f.n, thickness)\n );\n const placed = mapLocalToWorld(flap, u, v, n, hingeCorner);\n return ok(placed);\n}\n\n/** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */\nfunction mapLocalToWorld(s: Solid, uT: Vec3, vT: Vec3, nT: Vec3, origin: Vec3): Solid {\n const m: number[] = [uT[0], vT[0], nT[0], uT[1], vT[1], nT[1], uT[2], vT[2], nT[2]];\n const aa = matrixToAxisAngle(m);\n const rotated = aa.angleDeg === 0 ? s : rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });\n return translate(rotated, origin);\n}\n\n/** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */\nfunction matrixToAxisAngle(m: number[]): { axis: Vec3; angleDeg: number } {\n const m00 = m[0] ?? 0;\n const m01 = m[1] ?? 0;\n const m02 = m[2] ?? 0;\n const m10 = m[3] ?? 0;\n const m11 = m[4] ?? 0;\n const m12 = m[5] ?? 0;\n const m20 = m[6] ?? 0;\n const m21 = m[7] ?? 0;\n const m22 = m[8] ?? 0;\n const trace = m00 + m11 + m22;\n const cos = Math.max(-1, Math.min(1, (trace - 1) / 2));\n const angle = Math.acos(cos);\n const angleDeg = (angle * 180) / Math.PI;\n if (angle < 1e-9) return { axis: [0, 0, 1], angleDeg: 0 };\n if (Math.PI - angle < 1e-6) {\n const xx = (m00 + 1) / 2;\n const yy = (m11 + 1) / 2;\n const zz = (m22 + 1) / 2;\n let axis: Vec3;\n if (xx >= yy && xx >= zz) {\n const x = Math.sqrt(Math.max(xx, 0));\n axis = [x, (m01 + m10) / (4 * x), (m02 + m20) / (4 * x)];\n } else if (yy >= zz) {\n const y = Math.sqrt(Math.max(yy, 0));\n axis = [(m01 + m10) / (4 * y), y, (m12 + m21) / (4 * y)];\n } else {\n const z = Math.sqrt(Math.max(zz, 0));\n axis = [(m02 + m20) / (4 * z), (m12 + m21) / (4 * z), z];\n }\n return { axis: vecNormalize(axis), angleDeg: 180 };\n }\n const denom = 2 * Math.sin(angle);\n const axis: Vec3 = [(m21 - m12) / denom, (m02 - m20) / denom, (m10 - m01) / denom];\n return { axis: vecNormalize(axis), angleDeg };\n}\n\n/** Region-local circle loop (CCW) centred at `c` of radius `r`. */\nfunction circleLocal(c: Pt2, r: number): Pt2[] {\n const pts: Pt2[] = [];\n for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {\n const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;\n pts.push([c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)]);\n }\n return pts;\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import { type Result } from '@faicad/faijs/brepjs-compat';
2
+ import type { HemSpec, SheetMetalPart } from './types.js';
3
+ /**
4
+ * Author a hem: fold a region edge back ~180°+ onto its parent flat, then run a
5
+ * short return leg. Built on the contour-flange segment chainer — the curl is one
6
+ * or more ≤120° sub-arcs (each a recorded `hem::<id>::<n>` bend) followed by a flat
7
+ * return — so the development is EXACT: Σ curl bend allowances (via the table-aware
8
+ * {@link developedLength}) + the return length, laid out straight past the edge.
9
+ * Four `type`s set the curl/gap geometry (see {@link HemSpec}). Construction stays
10
+ * on the public, OCCT-WASM-safe API; the result is guarded to a valid single solid.
11
+ *
12
+ * @param part - the sheet metal part to attach a hem to.
13
+ * @param spec - the hem specification.
14
+ * @returns the updated part with the hem feature recorded, or an error.
15
+ */
16
+ export declare function hem(part: SheetMetalPart, spec: HemSpec): Result<SheetMetalPart>;
17
+ //# sourceMappingURL=hemFns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hemFns.d.ts","sourceRoot":"","sources":["../src/hemFns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,6BAA6B,CAAC;AAChG,OAAO,KAAK,EAIV,OAAO,EACP,cAAc,EACf,MAAM,YAAY,CAAC;AAmCpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,CAqH/E"}