brepjs 12.2.3 → 12.2.5

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 (64) hide show
  1. package/LICENSE +669 -190
  2. package/README.md +2 -2
  3. package/dist/2d.cjs +2 -2
  4. package/dist/2d.js +3 -3
  5. package/dist/{Blueprint-CFdLzoy5.cjs → Blueprint-CX2oh-NE.cjs} +5 -5
  6. package/dist/{Blueprint-DQ18yiAI.js → Blueprint-DlfUXrh0.js} +5 -5
  7. package/dist/{boolean2D-Dq3HF-Pd.js → boolean2D-COkYlAPe.js} +7 -7
  8. package/dist/{boolean2D-C5tjWX5T.cjs → boolean2D-G9edb4Pw.cjs} +7 -7
  9. package/dist/{booleanFns-DdU6eLxI.js → booleanFns-5MroD4TZ.js} +3 -3
  10. package/dist/{booleanFns-PbcD1St_.cjs → booleanFns-zdaV6L6c.cjs} +3 -3
  11. package/dist/brepjs.cjs +4223 -17
  12. package/dist/brepjs.js +4237 -31
  13. package/dist/core/shapeTypes.d.ts.map +1 -1
  14. package/dist/core.cjs +1 -1
  15. package/dist/core.js +1 -1
  16. package/dist/{cornerFinder-Dv3C7Yen.js → cornerFinder-CGzCuJy3.js} +1 -1
  17. package/dist/{cornerFinder-BphV9OLn.cjs → cornerFinder-NbeO5KWC.cjs} +1 -1
  18. package/dist/{curveFns-CeHbFVDc.cjs → curveFns-BN9bWCIl.cjs} +1 -1
  19. package/dist/{curveFns-GOcBuoCc.js → curveFns-DRb_8jyd.js} +1 -1
  20. package/dist/{drawFns-F5uDtd2v.cjs → drawFns-Br57GZWI.cjs} +11 -11
  21. package/dist/{drawFns-7fN_tn1I.js → drawFns-DsB3t0zE.js} +11 -11
  22. package/dist/{faceFns-Bhlm333o.cjs → faceFns-Cfvj4nxx.cjs} +1 -1
  23. package/dist/{faceFns-g5hzl_xe.js → faceFns-Dka8xvYk.js} +1 -1
  24. package/dist/{helpers-D3jdmQoR.cjs → helpers-Cu_7Qo9J.cjs} +4 -4
  25. package/dist/{helpers-Vqsg_fss.js → helpers-TIGqZBj7.js} +4 -4
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/io.cjs +3 -3
  29. package/dist/io.js +3 -3
  30. package/dist/kernel/brepkitAdapter.d.ts +1 -0
  31. package/dist/kernel/brepkitAdapter.d.ts.map +1 -1
  32. package/dist/kernel/brepkitWasmTypes.d.ts +43 -1
  33. package/dist/kernel/brepkitWasmTypes.d.ts.map +1 -1
  34. package/dist/kernel/types.d.ts +19 -5
  35. package/dist/kernel/types.d.ts.map +1 -1
  36. package/dist/{loft-B3ZDn94G.cjs → loft-BDnb8toT.cjs} +4 -4
  37. package/dist/{loft-BGHICbBS.js → loft-DTFP5fVL.js} +4 -4
  38. package/dist/{measurement-Ce4oE8Fg.js → measurement-9v764TRE.js} +2 -2
  39. package/dist/{measurement--jEL3ePF.cjs → measurement-ypLLNq5y.cjs} +2 -2
  40. package/dist/measurement.cjs +1 -1
  41. package/dist/measurement.js +1 -1
  42. package/dist/{meshFns-bHjCcOZp.js → meshFns-B_yFbSte.js} +2 -2
  43. package/dist/{meshFns-rTBykpgO.cjs → meshFns-xWim4bEU.cjs} +2 -2
  44. package/dist/{operations-D3BtYsAB.js → operations-2LVG2jh1.js} +5 -5
  45. package/dist/{operations-Cb4gWPwL.cjs → operations-D4Q6JrTP.cjs} +5 -5
  46. package/dist/operations.cjs +2 -2
  47. package/dist/operations.js +2 -2
  48. package/dist/query.cjs +4 -4
  49. package/dist/query.js +5 -5
  50. package/dist/{shapeFns-smSl7yhL.js → shapeFns-BP-fxfKw.js} +2 -2
  51. package/dist/{shapeFns-BHSGfqFS.cjs → shapeFns-ZbzAD8yC.cjs} +2 -2
  52. package/dist/{shapeTypes-BzrjB5be.cjs → shapeTypes-BQGbhdgi.cjs} +2 -1
  53. package/dist/{shapeTypes-Py3LEyDe.js → shapeTypes-ByN4-0Hp.js} +2 -1
  54. package/dist/sketching.cjs +2 -2
  55. package/dist/sketching.js +2 -2
  56. package/dist/{solidBuilders-Jh5Bnm02.js → solidBuilders-Bc6kgqJ9.js} +2 -2
  57. package/dist/{solidBuilders-DINhvxLC.cjs → solidBuilders-CYEEZgiP.cjs} +2 -2
  58. package/dist/{surfaceBuilders-CD_9kc1m.js → surfaceBuilders-Bdn7iqv5.js} +2 -2
  59. package/dist/{surfaceBuilders-BCd-ugN2.cjs → surfaceBuilders-CXarnCh8.cjs} +2 -2
  60. package/dist/{topology-B15z3Ts4.cjs → topology-BS3_AnBF.cjs} +8 -8
  61. package/dist/{topology-DmQY6469.js → topology-CqhGW5sd.js} +8 -8
  62. package/dist/topology.cjs +6 -6
  63. package/dist/topology.js +6 -6
  64. package/package.json +9 -5
package/dist/brepjs.js CHANGED
@@ -43,49 +43,4254 @@ var __callDispose = (stack, error, hasError) => {
43
43
  };
44
44
  return next();
45
45
  };
46
- import { c as castShape, D as DisposalScope, B as getKernel, k as isFace, q as isShell, r as isSolid, p as isShape3D, F as createSolid, G as createWire, j as isEdge, u as isWire } from "./shapeTypes-Py3LEyDe.js";
47
- import { M, N, O, a, C, H, I, b, d, P, E, g, Q, i, e, f, h, l, m, n, o, s, t, v, w, R, x, S, y, z, A } from "./shapeTypes-Py3LEyDe.js";
48
- import { o as ok, z as translateKernelError, e as err, p as typeCastError, q as queryError, y as validationError, B as BrepErrorCode, k as kernelError, i as ioError, d as isErr, r as unwrap } from "./errors-B7kgv0cd.js";
46
+ import { c as castShape, D as DisposalScope, B as getKernel, k as isFace, q as isShell, r as isSolid, p as isShape3D, F as createSolid, G as createWire, j as isEdge, u as isWire } from "./shapeTypes-ByN4-0Hp.js";
47
+ import { M, N, O, a, C, H, I, b, d, P, E, g, Q, i, e, f, h, l, m, n, o, s, t, v, w, R, x, S, y, z, A } from "./shapeTypes-ByN4-0Hp.js";
48
+ import { o as ok, z as translateKernelError, e as err, p as typeCastError, q as queryError, y as validationError, B as BrepErrorCode, k as kernelError, i as ioError, d as isErr, r as unwrap$1 } from "./errors-B7kgv0cd.js";
49
49
  import { O as O2, A as A2, a as a2, c, b as b2, f as f2, C as C2, g as g2, m as m2, h as h2, j, l as l2, D, E as E2, F, s as s2, G, H as H2, t as t2, n as n2, u, v as v2, w as w2, x as x2, I as I2 } from "./errors-B7kgv0cd.js";
50
50
  import { c as vecDistance, H as HASH_CODE_MAX, n as vecScale, j as vecNormalize, f as vecIsZero } from "./vecOps-ZDdZWbwT.js";
51
51
  import { D as D2, R as R2, v as v3, a as a3, b as b3, d as d2, e as e2, g as g3, h as h3, i as i2, k, l as l3, m as m3, o as o2 } from "./vecOps-ZDdZWbwT.js";
52
- import { B, d as d3, C as C3, t as t3 } from "./Blueprint-DQ18yiAI.js";
53
- import { b as b4, g as g4, r } from "./loft-BGHICbBS.js";
54
- import { w as walkAssembly, e as extrude$1, r as revolve$1, s as sweep, c as circularPattern, l as linearPattern } from "./operations-D3BtYsAB.js";
55
- import { a as a4, b as b5, d as d4, f as f3, g as g5, h as h4, i as i3, j as j2, k as k2, m as m4, n as n3, o as o3, p, q, t as t4, u as u2, v as v4, x as x3, y as y2, z as z2, A as A3, B as B2, C as C4, D as D3, E as E3, F as F2, G as G2 } from "./operations-D3BtYsAB.js";
56
- import { B as B3, a as a5, e as e3, C as C5, F as F3, S as S2, f as f4, l as l4, g as g6, n as n4, k as k3, q as q2, o as o4, p as p2, r as r2 } from "./boolean2D-Dq3HF-Pd.js";
52
+ import { B, d as d3, C as C3, t as t3 } from "./Blueprint-DlfUXrh0.js";
53
+ import { b as b4, g as g4, r } from "./loft-DTFP5fVL.js";
54
+ import { w as walkAssembly, e as extrude$1, r as revolve$1, s as sweep, c as circularPattern, l as linearPattern } from "./operations-2LVG2jh1.js";
55
+ import { a as a4, b as b5, d as d4, f as f3, g as g5, h as h4, i as i3, j as j2, k as k2, m as m4, n as n3, o as o3, p, q, t as t4, u as u2, v as v4, x as x3, y as y2, z as z2, A as A3, B as B2, C as C4, D as D3, E as E3, F as F2, G as G2 } from "./operations-2LVG2jh1.js";
56
+ import { B as B3, a as a5, e as e3, C as C5, F as F3, S as S2, f as f4, l as l4, g as g6, n as n4, k as k3, q as q2, o as o4, p as p2, r as r2 } from "./boolean2D-COkYlAPe.js";
57
57
  import { createBlueprint, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, getBounds2D, getOrientation2D, isInside2D, mirror2D, reverseCurve, rotate2D, scale2D, sketch2DOnFace, sketch2DOnPlane, stretch2D, toSVGPathD, translate2D } from "./2d.js";
58
- import { l as createTypedFinder, m as faceFinder } from "./helpers-Vqsg_fss.js";
59
- import { g as g7 } from "./helpers-Vqsg_fss.js";
58
+ import { l as createTypedFinder, m as faceFinder } from "./helpers-TIGqZBj7.js";
59
+ import { g as g7 } from "./helpers-TIGqZBj7.js";
60
60
  import { blueprintToDXF, exportDXF, exportGlb, exportGltf, exportOBJ, exportThreeMF, importIGES, importSTEP, importSTL, importSVG, importSVGPathD } from "./io.js";
61
- import { C as C6, D as D4, a as a6, S as S3, b as b6, X, Y, c as c2, d as d5, e as e4, f as f5, Z, g as g8, h as h5, i as i4, j as j3, k as k4, l as l5, m as m5, n as n5, o as o5, p as p3, q as q3, r as r3, s as s3, t as t5, u as u3, v as v5, w as w3, x as x4, y as y3, z as z3, _, $, a0, a1, A as A4, a2 as a22, B as B4, E as E4, a3 as a32, F as F4, G as G3, H as H3, I as I3, J, K, L, M as M2, N as N2, O as O3, P as P2, Q as Q2, R as R3, T, U, a4 as a42, V, a5 as a52, a6 as a62, W } from "./drawFns-7fN_tn1I.js";
61
+ import { C as C6, D as D4, a as a6, S as S3, b as b6, X, Y, c as c2, d as d5, e as e4, f as f5, Z, g as g8, h as h5, i as i4, j as j3, k as k4, l as l5, m as m5, n as n5, o as o5, p as p3, q as q3, r as r3, s as s3, t as t5, u as u3, v as v5, w as w3, x as x4, y as y3, z as z3, _, $, a0, a1, A as A4, a2 as a22, B as B4, E as E4, a3 as a32, F as F4, G as G3, H as H3, I as I3, J, K, L, M as M2, N as N2, O as O3, P as P2, Q as Q2, R as R3, T, U, a4 as a42, V, a5 as a52, a6 as a62, W } from "./drawFns-DsB3t0zE.js";
62
62
  import { a as toVec3 } from "./types-CWDdqcrq.js";
63
63
  import { r as r4, t as t6 } from "./types-CWDdqcrq.js";
64
64
  import { c as c3, a as a7, p as p4, r as r5, t as t7 } from "./vectors-cec8p8NQ.js";
65
- import { v as vertexPosition, a as getFaces, e as getVertices, d as getEdges, h as getBounds, p as propagateOriginsFromEvolution, g as getFaceOrigins, j as applyMatrix$1, k as clone$1, l as describe$1, m as isEmpty$1, n as mirror$1, r as rotate$1, s as scale$1, o as simplify$1, t as toBREP$1, q as transformCopy$1, u as translate$1, c as getWires } from "./shapeFns-smSl7yhL.js";
66
- import { w as w4, f as f6, x as x5, i as i5, y as y4, z as z4, A as A5, B as B5, C as C7, D as D5 } from "./shapeFns-smSl7yhL.js";
67
- import { p as propagateFaceTagsFromEvolution, a as propagateColorsFromEvolution, h as hasFaceTags, b as hasColorMetadata, c as cut$1, f as fuse$1, i as intersect$1, s as section$1, d as sectionToFace$1, e as slice$1, g as split$1, j as fuseAll, k as cutAll } from "./booleanFns-DdU6eLxI.js";
68
- import { l as l6, m as m6, n as n6, o as o6, q as q4, r as r6, t as t8, u as u4, v as v6 } from "./booleanFns-DdU6eLxI.js";
69
- import { c as chamferDistAngle, h as heal$1, i as isValid$1 } from "./topology-DmQY6469.js";
70
- import { a as a8, b as b7, d as d6, e as e5, f as f7, g as g9, j as j4, k as k5, l as l7, m as m7, n as n7, o as o7, p as p5, q as q5, r as r7, s as s4, t as t9, u as u5, v as v7, w as w5, x as x6, y as y5, z as z5, A as A6, B as B6, C as C8, D as D6, E as E5, F as F5, G as G4, H as H4, I as I4, J as J2, K as K2, L as L2, M as M3, N as N3, O as O4, P as P3, Q as Q3 } from "./topology-DmQY6469.js";
71
- import { e as curveIsClosed, d as curveStartPoint, c as curveTangentAt, h as curvePointAt, a as curveEndPoint, i as curveLength } from "./curveFns-GOcBuoCc.js";
72
- import { j as j5, k as k6, l as l8, f as f8, b as b8, g as g10, m as m8, o as o8 } from "./curveFns-GOcBuoCc.js";
73
- import { i as iterTopo, e as faceCenter, n as normalAt, j as fromBREP$1, k as innerWires, o as outerWire, g as getSurfaceType } from "./faceFns-g5hzl_xe.js";
74
- import { l as l9, c as c4, m as m9, d as d7, f as f9, q as q6, r as r8, s as s5, p as p6, t as t10, v as v8, u as u6, h as h6 } from "./faceFns-g5hzl_xe.js";
75
- import { m as mesh$1, a as meshEdges$1 } from "./meshFns-bHjCcOZp.js";
76
- import { c as c5, b as b9, e as e6, d as d8, f as f10 } from "./meshFns-bHjCcOZp.js";
77
- import { m as measureArea, a as measureSurfaceProps, b as measureVolumeProps, c as measureVolume } from "./measurement-Ce4oE8Fg.js";
78
- import { d as d9, e as e7, f as f11, g as g11, h as h7, i as i6 } from "./measurement-Ce4oE8Fg.js";
79
- import { m as makeFace } from "./surfaceBuilders-CD_9kc1m.js";
80
- import { n as n8 } from "./surfaceBuilders-CD_9kc1m.js";
65
+ import { v as vertexPosition, a as getFaces, e as getVertices, d as getEdges, h as getBounds, p as propagateOriginsFromEvolution, g as getFaceOrigins, j as applyMatrix$1, k as clone$1, l as describe$1, m as isEmpty$1, n as mirror$1, r as rotate$1, s as scale$1, o as simplify$1, t as toBREP$1, q as transformCopy$1, u as translate$1, c as getWires } from "./shapeFns-BP-fxfKw.js";
66
+ import { w as w4, f as f6, x as x5, i as i5, y as y4, z as z4, A as A5, B as B5, C as C7, D as D5 } from "./shapeFns-BP-fxfKw.js";
67
+ import { p as propagateFaceTagsFromEvolution, a as propagateColorsFromEvolution, h as hasFaceTags, b as hasColorMetadata, c as cut$1, f as fuse$1, i as intersect$1, s as section$1, d as sectionToFace$1, e as slice$1, g as split$1, j as fuseAll, k as cutAll } from "./booleanFns-5MroD4TZ.js";
68
+ import { l as l6, m as m6, n as n6, o as o6, q as q4, r as r6, t as t8, u as u4, v as v6 } from "./booleanFns-5MroD4TZ.js";
69
+ import { c as chamferDistAngle, h as heal$1, i as isValid$1 } from "./topology-CqhGW5sd.js";
70
+ import { a as a8, b as b7, d as d6, e as e5, f as f7, g as g9, j as j4, k as k5, l as l7, m as m7, n as n7, o as o7, p as p5, q as q5, r as r7, s as s4, t as t9, u as u5, v as v7, w as w5, x as x6, y as y5, z as z5, A as A6, B as B6, C as C8, D as D6, E as E5, F as F5, G as G4, H as H4, I as I4, J as J2, K as K2, L as L2, M as M3, N as N3, O as O4, P as P3, Q as Q3 } from "./topology-CqhGW5sd.js";
71
+ import { e as curveIsClosed, d as curveStartPoint, c as curveTangentAt, h as curvePointAt, a as curveEndPoint, i as curveLength } from "./curveFns-DRb_8jyd.js";
72
+ import { j as j5, k as k6, l as l8, f as f8, b as b8, g as g10, m as m8, o as o8 } from "./curveFns-DRb_8jyd.js";
73
+ import { i as iterTopo, e as faceCenter, n as normalAt, j as fromBREP$1, k as innerWires, o as outerWire, g as getSurfaceType } from "./faceFns-Dka8xvYk.js";
74
+ import { l as l9, c as c4, m as m9, d as d7, f as f9, q as q6, r as r8, s as s5, p as p6, t as t10, v as v8, u as u6, h as h6 } from "./faceFns-Dka8xvYk.js";
75
+ import { m as mesh$1, a as meshEdges$1 } from "./meshFns-B_yFbSte.js";
76
+ import { c as c5, b as b9, e as e6, d as d8, f as f10 } from "./meshFns-B_yFbSte.js";
77
+ import { m as measureArea, a as measureSurfaceProps, b as measureVolumeProps, c as measureVolume } from "./measurement-9v764TRE.js";
78
+ import { d as d9, e as e7, f as f11, g as g11, h as h7, i as i6 } from "./measurement-9v764TRE.js";
79
+ import { m as makeFace } from "./surfaceBuilders-Bdn7iqv5.js";
80
+ import { n as n8 } from "./surfaceBuilders-Bdn7iqv5.js";
81
81
  import { edgeFinder } from "./query.js";
82
- import { c as makeCylinder } from "./solidBuilders-Jh5Bnm02.js";
82
+ import { c as makeCylinder } from "./solidBuilders-Bc6kgqJ9.js";
83
83
  import { BrepBugError, bug } from "./result.js";
84
- import { c as c6 } from "./cornerFinder-Dv3C7Yen.js";
84
+ import { c as c6 } from "./cornerFinder-CGzCuJy3.js";
85
85
  import { createOperationRegistry, createTaskQueue, createWorkerClient, createWorkerHandler, dequeueTask, enqueueTask, isDisposeRequest, isErrorResponse, isInitRequest, isOperationRequest, isQueueEmpty, isSuccessResponse, pendingCount, registerHandler, rejectAll } from "./worker.js";
86
86
  function supportsProjection(kernel) {
87
87
  return "projectShape" in kernel;
88
88
  }
89
+ function evaluateCurve2d(c7, t11) {
90
+ switch (c7.__bk2d) {
91
+ case "line":
92
+ return [c7.ox + c7.dx * t11, c7.oy + c7.dy * t11];
93
+ case "circle": {
94
+ const angle = c7.sense ? t11 : -t11;
95
+ return [c7.cx + c7.radius * Math.cos(angle), c7.cy + c7.radius * Math.sin(angle)];
96
+ }
97
+ case "ellipse": {
98
+ const angle = c7.sense ? t11 : -t11;
99
+ const cos = Math.cos(c7.xDirAngle);
100
+ const sin = Math.sin(c7.xDirAngle);
101
+ const x7 = c7.majorRadius * Math.cos(angle);
102
+ const y6 = c7.minorRadius * Math.sin(angle);
103
+ return [c7.cx + x7 * cos - y6 * sin, c7.cy + x7 * sin + y6 * cos];
104
+ }
105
+ case "bezier":
106
+ return evaluateBezier(c7.poles, t11);
107
+ case "bspline":
108
+ return evaluateBSpline2d(c7, t11);
109
+ case "trimmed": {
110
+ const mapped = c7.tStart + t11 * (c7.tEnd - c7.tStart);
111
+ return evaluateCurve2d(c7.basis, mapped);
112
+ }
113
+ }
114
+ }
115
+ function tangentCurve2d(c7, t11) {
116
+ switch (c7.__bk2d) {
117
+ case "line":
118
+ return [c7.dx, c7.dy];
119
+ case "circle": {
120
+ const angle = c7.sense ? t11 : -t11;
121
+ const sign = c7.sense ? 1 : -1;
122
+ return [-c7.radius * Math.sin(angle) * sign, c7.radius * Math.cos(angle) * sign];
123
+ }
124
+ case "ellipse": {
125
+ const angle = c7.sense ? t11 : -t11;
126
+ const sign = c7.sense ? 1 : -1;
127
+ const cos = Math.cos(c7.xDirAngle);
128
+ const sin = Math.sin(c7.xDirAngle);
129
+ const dx = -c7.majorRadius * Math.sin(angle) * sign;
130
+ const dy = c7.minorRadius * Math.cos(angle) * sign;
131
+ return [dx * cos - dy * sin, dx * sin + dy * cos];
132
+ }
133
+ case "bezier": {
134
+ const h8 = 1e-8;
135
+ const p0 = evaluateBezier(c7.poles, Math.max(0, t11 - h8));
136
+ const p1 = evaluateBezier(c7.poles, Math.min(1, t11 + h8));
137
+ const dt = Math.min(1, t11 + h8) - Math.max(0, t11 - h8);
138
+ return [(p1[0] - p0[0]) / dt, (p1[1] - p0[1]) / dt];
139
+ }
140
+ case "bspline": {
141
+ const h8 = 1e-8;
142
+ const kFirst = c7.knots[0];
143
+ const kLast = c7.knots[c7.knots.length - 1];
144
+ const p0 = evaluateBSpline2d(c7, Math.max(kFirst, t11 - h8));
145
+ const p1 = evaluateBSpline2d(c7, Math.min(kLast, t11 + h8));
146
+ const dt = Math.min(kLast, t11 + h8) - Math.max(kFirst, t11 - h8);
147
+ return [(p1[0] - p0[0]) / dt, (p1[1] - p0[1]) / dt];
148
+ }
149
+ case "trimmed": {
150
+ const mapped = c7.tStart + t11 * (c7.tEnd - c7.tStart);
151
+ const tan = tangentCurve2d(c7.basis, mapped);
152
+ const scale2 = c7.tEnd - c7.tStart;
153
+ return [tan[0] * scale2, tan[1] * scale2];
154
+ }
155
+ }
156
+ }
157
+ function curveBounds(c7) {
158
+ switch (c7.__bk2d) {
159
+ case "line":
160
+ return { first: 0, last: c7.len };
161
+ case "circle":
162
+ case "ellipse":
163
+ return { first: 0, last: 2 * Math.PI };
164
+ case "bezier":
165
+ return { first: 0, last: 1 };
166
+ case "bspline":
167
+ return { first: c7.knots[0], last: c7.knots[c7.knots.length - 1] };
168
+ case "trimmed":
169
+ return { first: 0, last: 1 };
170
+ }
171
+ }
172
+ function curveTypeName(c7) {
173
+ switch (c7.__bk2d) {
174
+ case "line":
175
+ return "LINE";
176
+ case "circle":
177
+ return "CIRCLE";
178
+ case "ellipse":
179
+ return "ELLIPSE";
180
+ case "bezier":
181
+ return "BEZIER_CURVE";
182
+ case "bspline":
183
+ return "BSPLINE_CURVE";
184
+ case "trimmed":
185
+ return "TRIMMED_" + curveTypeName(c7.basis);
186
+ }
187
+ }
188
+ function makeLine2d(x1, y1, x22, y22) {
189
+ const dx = x22 - x1;
190
+ const dy = y22 - y1;
191
+ const len = Math.sqrt(dx * dx + dy * dy);
192
+ return {
193
+ __bk2d: "line",
194
+ ox: x1,
195
+ oy: y1,
196
+ dx: len > 0 ? dx / len : 1,
197
+ dy: len > 0 ? dy / len : 0,
198
+ len
199
+ };
200
+ }
201
+ function makeCircle2d(cx, cy, radius, sense = true) {
202
+ return { __bk2d: "circle", cx, cy, radius, sense };
203
+ }
204
+ function makeEllipse2d(cx, cy, majorRadius, minorRadius, xDirX = 1, xDirY = 0, sense = true) {
205
+ return {
206
+ __bk2d: "ellipse",
207
+ cx,
208
+ cy,
209
+ majorRadius,
210
+ minorRadius,
211
+ xDirAngle: Math.atan2(xDirY, xDirX),
212
+ sense
213
+ };
214
+ }
215
+ function makeBezier2d(poles) {
216
+ return { __bk2d: "bezier", poles: [...poles] };
217
+ }
218
+ function translateCurve2d(c7, dx, dy) {
219
+ switch (c7.__bk2d) {
220
+ case "line":
221
+ return { ...c7, ox: c7.ox + dx, oy: c7.oy + dy };
222
+ case "circle":
223
+ return { ...c7, cx: c7.cx + dx, cy: c7.cy + dy };
224
+ case "ellipse":
225
+ return { ...c7, cx: c7.cx + dx, cy: c7.cy + dy };
226
+ case "bezier":
227
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => [x7 + dx, y6 + dy]) };
228
+ case "bspline":
229
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => [x7 + dx, y6 + dy]) };
230
+ case "trimmed":
231
+ return { ...c7, basis: translateCurve2d(c7.basis, dx, dy) };
232
+ }
233
+ }
234
+ function rotateCurve2d(c7, angle, cx, cy) {
235
+ const cos = Math.cos(angle);
236
+ const sin = Math.sin(angle);
237
+ const rotatePoint = (x7, y6) => {
238
+ const rx = x7 - cx;
239
+ const ry = y6 - cy;
240
+ return [cx + rx * cos - ry * sin, cy + rx * sin + ry * cos];
241
+ };
242
+ switch (c7.__bk2d) {
243
+ case "line": {
244
+ const [ox, oy] = rotatePoint(c7.ox, c7.oy);
245
+ const ndx = c7.dx * cos - c7.dy * sin;
246
+ const ndy = c7.dx * sin + c7.dy * cos;
247
+ return { ...c7, ox, oy, dx: ndx, dy: ndy };
248
+ }
249
+ case "circle": {
250
+ const [ncx, ncy] = rotatePoint(c7.cx, c7.cy);
251
+ return { ...c7, cx: ncx, cy: ncy };
252
+ }
253
+ case "ellipse": {
254
+ const [ncx, ncy] = rotatePoint(c7.cx, c7.cy);
255
+ return { ...c7, cx: ncx, cy: ncy, xDirAngle: c7.xDirAngle + angle };
256
+ }
257
+ case "bezier":
258
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => rotatePoint(x7, y6)) };
259
+ case "bspline":
260
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => rotatePoint(x7, y6)) };
261
+ case "trimmed":
262
+ return { ...c7, basis: rotateCurve2d(c7.basis, angle, cx, cy) };
263
+ }
264
+ }
265
+ function scaleCurve2d(c7, factor, cx, cy) {
266
+ const scalePoint = (x7, y6) => [
267
+ cx + (x7 - cx) * factor,
268
+ cy + (y6 - cy) * factor
269
+ ];
270
+ switch (c7.__bk2d) {
271
+ case "line": {
272
+ const [ox, oy] = scalePoint(c7.ox, c7.oy);
273
+ return { ...c7, ox, oy };
274
+ }
275
+ case "circle": {
276
+ const [ncx, ncy] = scalePoint(c7.cx, c7.cy);
277
+ return { ...c7, cx: ncx, cy: ncy, radius: c7.radius * Math.abs(factor) };
278
+ }
279
+ case "ellipse": {
280
+ const [ncx, ncy] = scalePoint(c7.cx, c7.cy);
281
+ return {
282
+ ...c7,
283
+ cx: ncx,
284
+ cy: ncy,
285
+ majorRadius: c7.majorRadius * Math.abs(factor),
286
+ minorRadius: c7.minorRadius * Math.abs(factor)
287
+ };
288
+ }
289
+ case "bezier":
290
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => scalePoint(x7, y6)) };
291
+ case "bspline":
292
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => scalePoint(x7, y6)) };
293
+ case "trimmed":
294
+ return { ...c7, basis: scaleCurve2d(c7.basis, factor, cx, cy) };
295
+ }
296
+ }
297
+ function mirrorAtPoint(c7, cx, cy) {
298
+ return scaleCurve2d(c7, -1, cx, cy);
299
+ }
300
+ function mirrorAcrossAxis(c7, ox, oy, dx, dy) {
301
+ const len = Math.sqrt(dx * dx + dy * dy);
302
+ const nx = dx / len;
303
+ const ny = dy / len;
304
+ const reflectPoint = (x7, y6) => {
305
+ const rx = x7 - ox;
306
+ const ry = y6 - oy;
307
+ const dot = rx * nx + ry * ny;
308
+ return [ox + 2 * dot * nx - rx, oy + 2 * dot * ny - ry];
309
+ };
310
+ switch (c7.__bk2d) {
311
+ case "line": {
312
+ const [nox, noy] = reflectPoint(c7.ox, c7.oy);
313
+ const ndx = 2 * (c7.dx * nx + c7.dy * ny) * nx - c7.dx;
314
+ const ndy = 2 * (c7.dx * nx + c7.dy * ny) * ny - c7.dy;
315
+ return { ...c7, ox: nox, oy: noy, dx: ndx, dy: ndy };
316
+ }
317
+ case "circle": {
318
+ const [ncx, ncy] = reflectPoint(c7.cx, c7.cy);
319
+ return { ...c7, cx: ncx, cy: ncy, sense: !c7.sense };
320
+ }
321
+ case "ellipse": {
322
+ const [ncx, ncy] = reflectPoint(c7.cx, c7.cy);
323
+ const cos2 = nx * nx - ny * ny;
324
+ const sin2 = 2 * nx * ny;
325
+ const newAngle = Math.atan2(
326
+ sin2 * Math.cos(c7.xDirAngle) - cos2 * Math.sin(c7.xDirAngle),
327
+ cos2 * Math.cos(c7.xDirAngle) + sin2 * Math.sin(c7.xDirAngle)
328
+ );
329
+ return { ...c7, cx: ncx, cy: ncy, xDirAngle: newAngle, sense: !c7.sense };
330
+ }
331
+ case "bezier":
332
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => reflectPoint(x7, y6)) };
333
+ case "bspline":
334
+ return { ...c7, poles: c7.poles.map(([x7, y6]) => reflectPoint(x7, y6)) };
335
+ case "trimmed":
336
+ return { ...c7, basis: mirrorAcrossAxis(c7.basis, ox, oy, dx, dy) };
337
+ }
338
+ }
339
+ function intersectCurves2dFn(c1, c22, tolerance) {
340
+ const b1 = unwrapCurve(c1);
341
+ const b22 = unwrapCurve(c22);
342
+ if (b1.__bk2d === "line" && b22.__bk2d === "line") {
343
+ return intersectLineLine(c1, b1, c22, b22, tolerance);
344
+ }
345
+ if (b1.__bk2d === "line" && b22.__bk2d === "circle") {
346
+ return { points: intersectLineCircle(c1, b1, c22, b22, tolerance), segments: [] };
347
+ }
348
+ if (b1.__bk2d === "circle" && b22.__bk2d === "line") {
349
+ return { points: intersectLineCircle(c22, b22, c1, b1, tolerance), segments: [] };
350
+ }
351
+ if (b1.__bk2d === "circle" && b22.__bk2d === "circle") {
352
+ return { points: intersectCircleCircle(c1, b1, c22, b22, tolerance), segments: [] };
353
+ }
354
+ const isSelf = c1 === c22;
355
+ return numericalIntersect(c1, c22, tolerance, isSelf);
356
+ }
357
+ function unwrapCurve(c7) {
358
+ let cur = c7;
359
+ while (cur.__bk2d === "trimmed") cur = cur.basis;
360
+ return cur;
361
+ }
362
+ function inDomain(c7, t11, tol) {
363
+ const b10 = curveBounds(c7);
364
+ return t11 >= b10.first - tol && t11 <= b10.last + tol;
365
+ }
366
+ function refineParam(c7, px, py) {
367
+ const bounds = curveBounds(c7);
368
+ if (!isFinite(bounds.first) || !isFinite(bounds.last)) return null;
369
+ const N4 = 80;
370
+ const dt = (bounds.last - bounds.first) / N4;
371
+ let bestT = bounds.first;
372
+ let bestD = Infinity;
373
+ for (let i7 = 0; i7 <= N4; i7++) {
374
+ const t11 = bounds.first + i7 * dt;
375
+ const [ex2, ey2] = evaluateCurve2d(c7, t11);
376
+ const d10 = (ex2 - px) ** 2 + (ey2 - py) ** 2;
377
+ if (d10 < bestD) {
378
+ bestD = d10;
379
+ bestT = t11;
380
+ }
381
+ }
382
+ const [sx, sy] = evaluateCurve2d(c7, bounds.first);
383
+ const [ex, ey] = evaluateCurve2d(c7, bounds.last);
384
+ const [mx, my] = evaluateCurve2d(c7, (bounds.first + bounds.last) / 2);
385
+ const geomExtent = Math.max(
386
+ Math.sqrt((ex - sx) ** 2 + (ey - sy) ** 2),
387
+ Math.sqrt((mx - sx) ** 2 + (my - sy) ** 2),
388
+ 1e-6
389
+ );
390
+ const maxDist = geomExtent * 0.1;
391
+ return bestD < maxDist * maxDist ? bestT : null;
392
+ }
393
+ function intersectLineLine(c1, l1, c22, l22, tol) {
394
+ const det = l1.dx * l22.dy - l1.dy * l22.dx;
395
+ if (Math.abs(det) >= 1e-14) {
396
+ const ex3 = l22.ox - l1.ox;
397
+ const ey3 = l22.oy - l1.oy;
398
+ const t1 = (ex3 * l22.dy - ey3 * l22.dx) / det;
399
+ const t22 = (ex3 * l1.dy - ey3 * l1.dx) / det;
400
+ if (!inDomain(c1, t1, tol) || !inDomain(c22, t22, tol)) return { points: [], segments: [] };
401
+ return { points: [[l1.ox + t1 * l1.dx, l1.oy + t1 * l1.dy]], segments: [] };
402
+ }
403
+ const ex = l22.ox - l1.ox;
404
+ const ey = l22.oy - l1.oy;
405
+ const cross = ex * l1.dy - ey * l1.dx;
406
+ if (Math.abs(cross) > tol) return { points: [], segments: [] };
407
+ const b1 = curveBounds(c1);
408
+ const b22 = curveBounds(c22);
409
+ const p2s = evaluateCurve2d(c22, b22.first);
410
+ const p2e = evaluateCurve2d(c22, b22.last);
411
+ const t2sOn1 = (p2s[0] - l1.ox) * l1.dx + (p2s[1] - l1.oy) * l1.dy;
412
+ const t2eOn1 = (p2e[0] - l1.ox) * l1.dx + (p2e[1] - l1.oy) * l1.dy;
413
+ const overlapStart = Math.max(b1.first, Math.min(t2sOn1, t2eOn1));
414
+ const overlapEnd = Math.min(b1.last, Math.max(t2sOn1, t2eOn1));
415
+ if (overlapEnd - overlapStart < tol) return { points: [], segments: [] };
416
+ const sx = l1.ox + overlapStart * l1.dx;
417
+ const sy = l1.oy + overlapStart * l1.dy;
418
+ const ex2 = l1.ox + overlapEnd * l1.dx;
419
+ const ey2 = l1.oy + overlapEnd * l1.dy;
420
+ const seg = makeLine2d(sx, sy, ex2, ey2);
421
+ return { points: [], segments: [seg] };
422
+ }
423
+ function intersectLineCircle(cLine, line, cCirc, circ, tol) {
424
+ const fx = line.ox - circ.cx;
425
+ const fy = line.oy - circ.cy;
426
+ const a9 = line.dx * line.dx + line.dy * line.dy;
427
+ const b10 = 2 * (fx * line.dx + fy * line.dy);
428
+ const c7 = fx * fx + fy * fy - circ.radius * circ.radius;
429
+ const disc = b10 * b10 - 4 * a9 * c7;
430
+ if (disc < -tol) return [];
431
+ const results = [];
432
+ const sqrtDisc = Math.sqrt(Math.max(0, disc));
433
+ const t1 = (-b10 - sqrtDisc) / (2 * a9);
434
+ const t22 = (-b10 + sqrtDisc) / (2 * a9);
435
+ for (const tLine of disc < tol * tol ? [t1] : [t1, t22]) {
436
+ if (!inDomain(cLine, tLine, tol)) continue;
437
+ const px = line.ox + tLine * line.dx;
438
+ const py = line.oy + tLine * line.dy;
439
+ const tCirc = refineParam(cCirc, px, py);
440
+ if (tCirc === null) continue;
441
+ const [cx2, cy2] = evaluateCurve2d(cCirc, tCirc);
442
+ if ((cx2 - px) ** 2 + (cy2 - py) ** 2 > tol * tol * 1e6) continue;
443
+ results.push([px, py]);
444
+ }
445
+ return results;
446
+ }
447
+ function intersectCircleCircle(c1, circ1, c22, circ2, tol) {
448
+ const dx = circ2.cx - circ1.cx;
449
+ const dy = circ2.cy - circ1.cy;
450
+ const d10 = Math.sqrt(dx * dx + dy * dy);
451
+ if (d10 > circ1.radius + circ2.radius + tol) return [];
452
+ if (d10 < Math.abs(circ1.radius - circ2.radius) - tol) return [];
453
+ if (d10 < 1e-14) return [];
454
+ const a9 = (circ1.radius * circ1.radius - circ2.radius * circ2.radius + d10 * d10) / (2 * d10);
455
+ const h22 = circ1.radius * circ1.radius - a9 * a9;
456
+ const h8 = Math.sqrt(Math.max(0, h22));
457
+ const mx = circ1.cx + a9 * dx / d10;
458
+ const my = circ1.cy + a9 * dy / d10;
459
+ const candidates = h8 < tol ? [[mx, my]] : [
460
+ [mx + h8 * dy / d10, my - h8 * dx / d10],
461
+ [mx - h8 * dy / d10, my + h8 * dx / d10]
462
+ ];
463
+ const results = [];
464
+ for (const [px, py] of candidates) {
465
+ const t1 = refineParam(c1, px, py);
466
+ const t22 = refineParam(c22, px, py);
467
+ if (t1 === null || t22 === null) continue;
468
+ const [x1, y1] = evaluateCurve2d(c1, t1);
469
+ const [x22, y22] = evaluateCurve2d(c22, t22);
470
+ const tolSq = (tol * 10) ** 2;
471
+ if ((x1 - px) ** 2 + (y1 - py) ** 2 > tolSq) continue;
472
+ if ((x22 - px) ** 2 + (y22 - py) ** 2 > tolSq) continue;
473
+ results.push([px, py]);
474
+ }
475
+ return results;
476
+ }
477
+ function numericalIntersect(c1, c22, tolerance, isSelf = false) {
478
+ const b1 = curveBounds(c1);
479
+ const b22 = curveBounds(c22);
480
+ if (!isFinite(b1.first) || !isFinite(b1.last) || !isFinite(b22.first) || !isFinite(b22.last)) {
481
+ return { points: [], segments: [] };
482
+ }
483
+ const N4 = 100;
484
+ const pts1 = [];
485
+ const pts2 = [];
486
+ for (let i7 = 0; i7 <= N4; i7++) {
487
+ const t1 = b1.first + (b1.last - b1.first) * i7 / N4;
488
+ const [x1, y1] = evaluateCurve2d(c1, t1);
489
+ pts1.push({ t: t1, x: x1, y: y1 });
490
+ const t22 = b22.first + (b22.last - b22.first) * i7 / N4;
491
+ const [x22, y22] = evaluateCurve2d(c22, t22);
492
+ pts2.push({ t: t22, x: x22, y: y22 });
493
+ }
494
+ const crossTol = Math.max(tolerance * 100, 0.5);
495
+ const candidates = [];
496
+ for (let i7 = 0; i7 < N4; i7++) {
497
+ const p1a = pts1[i7];
498
+ const p1b = pts1[i7 + 1];
499
+ for (let j6 = 0; j6 < N4; j6++) {
500
+ const p2a = pts2[j6];
501
+ const p2b = pts2[j6 + 1];
502
+ const x1min = Math.min(p1a.x, p1b.x) - crossTol;
503
+ const x1max = Math.max(p1a.x, p1b.x) + crossTol;
504
+ const y1min = Math.min(p1a.y, p1b.y) - crossTol;
505
+ const y1max = Math.max(p1a.y, p1b.y) + crossTol;
506
+ const x2min = Math.min(p2a.x, p2b.x);
507
+ const x2max = Math.max(p2a.x, p2b.x);
508
+ const y2min = Math.min(p2a.y, p2b.y);
509
+ const y2max = Math.max(p2a.y, p2b.y);
510
+ if (x1max < x2min || x2max < x1min || y1max < y2min || y2max < y1min) continue;
511
+ const t1mid = (p1a.t + p1b.t) / 2;
512
+ const t2mid = (p2a.t + p2b.t) / 2;
513
+ if (isSelf && Math.abs(t1mid - t2mid) < (b1.last - b1.first) / 5) continue;
514
+ candidates.push({ t1: t1mid, t2: t2mid });
515
+ }
516
+ }
517
+ const tol2 = tolerance * tolerance;
518
+ const found = [];
519
+ for (const { t1: t1Init, t2: t2Init } of candidates) {
520
+ let t1 = t1Init;
521
+ let t22 = t2Init;
522
+ for (let iter = 0; iter < 20; iter++) {
523
+ const [x12, y12] = evaluateCurve2d(c1, t1);
524
+ const [x222, y222] = evaluateCurve2d(c22, t22);
525
+ const dx = x12 - x222;
526
+ const dy = y12 - y222;
527
+ if (dx * dx + dy * dy < tol2) break;
528
+ const d1 = tangentCurve2d(c1, t1);
529
+ const d22 = tangentCurve2d(c22, t22);
530
+ const det = d1[0] * -d22[1] - -d22[0] * d1[1];
531
+ if (Math.abs(det) < 1e-14) break;
532
+ const dt1 = (-dx * -d22[1] - -dy * -d22[0]) / det;
533
+ const dt2 = (d1[0] * -dy - d1[1] * -dx) / det;
534
+ t1 += dt1;
535
+ t22 += dt2;
536
+ t1 = Math.max(b1.first, Math.min(b1.last, t1));
537
+ t22 = Math.max(b22.first, Math.min(b22.last, t22));
538
+ }
539
+ const [x1, y1] = evaluateCurve2d(c1, t1);
540
+ const [x22, y22] = evaluateCurve2d(c22, t22);
541
+ if (isSelf && Math.abs(t1 - t22) < (b1.last - b1.first) * 0.05) continue;
542
+ if ((x1 - x22) ** 2 + (y1 - y22) ** 2 < tolerance * tolerance * 1e6) {
543
+ const px = (x1 + x22) / 2;
544
+ const py = (y1 + y22) / 2;
545
+ let dup = false;
546
+ for (const [fx, fy] of found) {
547
+ if ((fx - px) ** 2 + (fy - py) ** 2 < tolerance * tolerance * 1e4) {
548
+ dup = true;
549
+ break;
550
+ }
551
+ }
552
+ if (!dup) found.push([px, py]);
553
+ }
554
+ }
555
+ return { points: found, segments: [] };
556
+ }
557
+ function serializeCurve2d(c7) {
558
+ return JSON.stringify(c7);
559
+ }
560
+ function deserializeCurve2d(data) {
561
+ return JSON.parse(data);
562
+ }
563
+ function createBBox2d() {
564
+ return { __bk2d_bbox: true, xMin: Infinity, yMin: Infinity, xMax: -Infinity, yMax: -Infinity };
565
+ }
566
+ function addCurveToBBox(bbox, c7, _tol) {
567
+ const bounds = curveBounds(c7);
568
+ if (!isFinite(bounds.first) || !isFinite(bounds.last)) return;
569
+ const nSamples = 20;
570
+ const dt = (bounds.last - bounds.first) / nSamples;
571
+ for (let i7 = 0; i7 <= nSamples; i7++) {
572
+ const t11 = bounds.first + i7 * dt;
573
+ const [x7, y6] = evaluateCurve2d(c7, t11);
574
+ if (x7 < bbox.xMin) bbox.xMin = x7;
575
+ if (y6 < bbox.yMin) bbox.yMin = y6;
576
+ if (x7 > bbox.xMax) bbox.xMax = x7;
577
+ if (y6 > bbox.yMax) bbox.yMax = y6;
578
+ }
579
+ }
580
+ function evaluateBezier(poles, t11) {
581
+ const n9 = poles.length;
582
+ const work = poles.map(([x7, y6]) => [x7, y6]);
583
+ for (let r9 = 1; r9 < n9; r9++) {
584
+ for (let i7 = 0; i7 < n9 - r9; i7++) {
585
+ const wi = work[i7];
586
+ const wi1 = work[i7 + 1];
587
+ wi[0] = (1 - t11) * wi[0] + t11 * wi1[0];
588
+ wi[1] = (1 - t11) * wi[1] + t11 * wi1[1];
589
+ }
590
+ }
591
+ return work[0];
592
+ }
593
+ function evaluateBSpline2d(c7, t11) {
594
+ const fullKnots = [];
595
+ for (let i7 = 0; i7 < c7.knots.length; i7++) {
596
+ const mult = c7.multiplicities[i7] ?? 1;
597
+ for (let j6 = 0; j6 < mult; j6++) {
598
+ fullKnots.push(c7.knots[i7]);
599
+ }
600
+ }
601
+ const p7 = c7.degree;
602
+ const n9 = c7.poles.length;
603
+ const k7 = fullKnots.length;
604
+ const tClamped = Math.max(fullKnots[p7], Math.min(fullKnots[k7 - p7 - 1], t11));
605
+ let span = p7;
606
+ for (let i7 = p7; i7 < k7 - p7 - 1; i7++) {
607
+ if (tClamped >= fullKnots[i7] && tClamped < fullKnots[i7 + 1]) {
608
+ span = i7;
609
+ break;
610
+ }
611
+ }
612
+ if (tClamped >= fullKnots[k7 - p7 - 1]) span = k7 - p7 - 2;
613
+ const d10 = [];
614
+ for (let j6 = 0; j6 <= p7; j6++) {
615
+ const idx = Math.min(span - p7 + j6, n9 - 1);
616
+ const pole = c7.poles[Math.max(0, idx)];
617
+ d10.push([pole[0], pole[1]]);
618
+ }
619
+ for (let r9 = 1; r9 <= p7; r9++) {
620
+ for (let j6 = p7; j6 >= r9; j6--) {
621
+ const i7 = span - p7 + j6;
622
+ const left = fullKnots[i7] ?? 0;
623
+ const right = fullKnots[i7 + p7 - r9 + 1] ?? 1;
624
+ const denom = right - left;
625
+ const alpha = denom > 1e-15 ? (tClamped - left) / denom : 0;
626
+ const dj = d10[j6];
627
+ const djPrev = d10[j6 - 1];
628
+ dj[0] = (1 - alpha) * djPrev[0] + alpha * dj[0];
629
+ dj[1] = (1 - alpha) * djPrev[1] + alpha * dj[1];
630
+ }
631
+ }
632
+ return d10[p7];
633
+ }
634
+ function isBrepkitHandle(shape2) {
635
+ return shape2 !== null && shape2 !== void 0 && typeof shape2 === "object" && shape2.__brepkit;
636
+ }
637
+ const noop = () => {
638
+ };
639
+ function handle(type, id) {
640
+ return {
641
+ __brepkit: true,
642
+ type,
643
+ id,
644
+ delete: noop,
645
+ HashCode(upperBound) {
646
+ return id % upperBound;
647
+ },
648
+ IsNull() {
649
+ return false;
650
+ }
651
+ };
652
+ }
653
+ function solidHandle(id) {
654
+ return handle("solid", id);
655
+ }
656
+ function faceHandle(id) {
657
+ return handle("face", id);
658
+ }
659
+ function edgeHandle(id) {
660
+ return handle("edge", id);
661
+ }
662
+ function wireHandle(id) {
663
+ return handle("wire", id);
664
+ }
665
+ function shellHandle(id) {
666
+ return handle("shell", id);
667
+ }
668
+ function compoundHandle(id) {
669
+ const h8 = handle("compound", id);
670
+ if (syntheticCompounds.has(id)) {
671
+ return { ...h8, delete: () => syntheticCompounds.delete(id) };
672
+ }
673
+ return h8;
674
+ }
675
+ function vertexHandle(id) {
676
+ return handle("vertex", id);
677
+ }
678
+ function unwrap(shape2, expected) {
679
+ if (!isBrepkitHandle(shape2)) {
680
+ throw new Error("brepkit: expected a BrepkitHandle, got " + typeof shape2);
681
+ }
682
+ if (expected && shape2.type !== expected) {
683
+ throw new Error(`brepkit: expected ${expected} handle, got ${shape2.type}`);
684
+ }
685
+ return shape2.id;
686
+ }
687
+ function toArray(ids) {
688
+ return Array.from(ids);
689
+ }
690
+ function unwrapSolidOrThrow(shape2, methodName) {
691
+ if (!isBrepkitHandle(shape2)) {
692
+ throw new Error("brepkit: expected a BrepkitHandle, got " + typeof shape2);
693
+ }
694
+ if (shape2.type !== "solid") {
695
+ throw new Error(
696
+ `brepkit: ${methodName} requires a solid, got ${shape2.type}. Consider using makeCompound() to combine shapes first.`
697
+ );
698
+ }
699
+ return shape2.id;
700
+ }
701
+ function unwrapSolidsForExport(bk, shape2, methodName) {
702
+ if (!isBrepkitHandle(shape2)) {
703
+ throw new Error("brepkit: expected a BrepkitHandle, got " + typeof shape2);
704
+ }
705
+ if (shape2.type === "solid") {
706
+ return [shape2.id];
707
+ }
708
+ if (shape2.type === "compound") {
709
+ const ids = toArray(bk.getCompoundSolids(shape2.id));
710
+ if (ids.length > 0) return ids;
711
+ throw new Error(`brepkit: ${methodName} received a compound with no solids.`);
712
+ }
713
+ throw new Error(
714
+ `brepkit: ${methodName} requires a solid or compound of solids, got ${shape2.type}.`
715
+ );
716
+ }
717
+ function dist3(x1, y1, z1, x22, y22, z22) {
718
+ const dx = x1 - x22, dy = y1 - y22, dz = z1 - z22;
719
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
720
+ }
721
+ function translationMatrix(x7, y6, z6) {
722
+ return [
723
+ 1,
724
+ 0,
725
+ 0,
726
+ x7,
727
+ 0,
728
+ 1,
729
+ 0,
730
+ y6,
731
+ 0,
732
+ 0,
733
+ 1,
734
+ z6,
735
+ 0,
736
+ 0,
737
+ 0,
738
+ 1
739
+ ];
740
+ }
741
+ function rotationMatrix(angleDeg, axis = [0, 0, 1], center = [0, 0, 0]) {
742
+ const rad = angleDeg * Math.PI / 180;
743
+ const c7 = Math.cos(rad);
744
+ const s6 = Math.sin(rad);
745
+ const t11 = 1 - c7;
746
+ const len = Math.sqrt(axis[0] ** 2 + axis[1] ** 2 + axis[2] ** 2);
747
+ const [ux, uy, uz] = [axis[0] / len, axis[1] / len, axis[2] / len];
748
+ const r00 = t11 * ux * ux + c7;
749
+ const r01 = t11 * ux * uy - s6 * uz;
750
+ const r02 = t11 * ux * uz + s6 * uy;
751
+ const r10 = t11 * uy * ux + s6 * uz;
752
+ const r11 = t11 * uy * uy + c7;
753
+ const r12 = t11 * uy * uz - s6 * ux;
754
+ const r20 = t11 * uz * ux - s6 * uy;
755
+ const r21 = t11 * uz * uy + s6 * ux;
756
+ const r22 = t11 * uz * uz + c7;
757
+ const [cx, cy, cz] = center;
758
+ const tx = cx - (r00 * cx + r01 * cy + r02 * cz);
759
+ const ty = cy - (r10 * cx + r11 * cy + r12 * cz);
760
+ const tz = cz - (r20 * cx + r21 * cy + r22 * cz);
761
+ return [
762
+ r00,
763
+ r01,
764
+ r02,
765
+ tx,
766
+ r10,
767
+ r11,
768
+ r12,
769
+ ty,
770
+ r20,
771
+ r21,
772
+ r22,
773
+ tz,
774
+ 0,
775
+ 0,
776
+ 0,
777
+ 1
778
+ ];
779
+ }
780
+ function scaleMatrix(center, factor) {
781
+ const [cx, cy, cz] = center;
782
+ const tx = cx * (1 - factor);
783
+ const ty = cy * (1 - factor);
784
+ const tz = cz * (1 - factor);
785
+ return [
786
+ factor,
787
+ 0,
788
+ 0,
789
+ tx,
790
+ 0,
791
+ factor,
792
+ 0,
793
+ ty,
794
+ 0,
795
+ 0,
796
+ factor,
797
+ tz,
798
+ 0,
799
+ 0,
800
+ 0,
801
+ 1
802
+ ];
803
+ }
804
+ function affineMatrix(linear, translation) {
805
+ return [
806
+ linear[0],
807
+ linear[1],
808
+ linear[2],
809
+ translation[0],
810
+ linear[3],
811
+ linear[4],
812
+ linear[5],
813
+ translation[1],
814
+ linear[6],
815
+ linear[7],
816
+ linear[8],
817
+ translation[2],
818
+ 0,
819
+ 0,
820
+ 0,
821
+ 1
822
+ ];
823
+ }
824
+ function mirrorMatrix(origin, normal) {
825
+ const [ox, oy, oz] = origin;
826
+ const len = Math.sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2);
827
+ const nx = normal[0] / len;
828
+ const ny = normal[1] / len;
829
+ const nz = normal[2] / len;
830
+ const d10 = 2 * (ox * nx + oy * ny + oz * nz);
831
+ return [
832
+ 1 - 2 * nx * nx,
833
+ -2 * nx * ny,
834
+ -2 * nx * nz,
835
+ d10 * nx,
836
+ -2 * ny * nx,
837
+ 1 - 2 * ny * ny,
838
+ -2 * ny * nz,
839
+ d10 * ny,
840
+ -2 * nz * nx,
841
+ -2 * nz * ny,
842
+ 1 - 2 * nz * nz,
843
+ d10 * nz,
844
+ 0,
845
+ 0,
846
+ 0,
847
+ 1
848
+ ];
849
+ }
850
+ const DEFAULT_DEFLECTION = 0.01;
851
+ const DEFAULT_SEGMENTS = 32;
852
+ let syntheticCompoundCounter = 9e5;
853
+ const syntheticCompounds = /* @__PURE__ */ new Map();
854
+ const _warned = /* @__PURE__ */ new Set();
855
+ function warnOnce(key, message) {
856
+ if (_warned.has(key)) return;
857
+ _warned.add(key);
858
+ console.warn(`brepkit: ${message}`);
859
+ }
860
+ function hasBooleanOptions(opts) {
861
+ return opts.optimisation !== void 0 || opts.simplify !== void 0 || opts.strategy !== void 0 || opts.fuzzyValue !== void 0;
862
+ }
863
+ class BrepkitAdapter {
864
+ oc;
865
+ kernelId = "brepkit";
866
+ /** The underlying brepkit WASM kernel instance (typed). */
867
+ bk;
868
+ constructor(brepkitKernel) {
869
+ this.bk = brepkitKernel;
870
+ this.oc = brepkitKernel;
871
+ }
872
+ // ═══════════════════════════════════════════════════════════════════════
873
+ // Boolean operations
874
+ // ═══════════════════════════════════════════════════════════════════════
875
+ fuse(shape2, tool, _options) {
876
+ if (_options && hasBooleanOptions(_options)) {
877
+ warnOnce(
878
+ "boolean-options",
879
+ "BooleanOptions (optimisation, simplify, strategy, fuzzyValue) not supported; ignored."
880
+ );
881
+ }
882
+ const baseId = unwrapSolidOrThrow(shape2, "fuse");
883
+ const toolHandle = tool;
884
+ if (toolHandle.type === "compound") {
885
+ const toolSolidIds = toArray(this.bk.getCompoundSolids(toolHandle.id));
886
+ let currentId = baseId;
887
+ for (const toolSolidId of toolSolidIds) {
888
+ currentId = this.bk.fuse(currentId, toolSolidId);
889
+ }
890
+ return solidHandle(currentId);
891
+ }
892
+ const result = this.bk.fuse(baseId, unwrapSolidOrThrow(tool, "fuse"));
893
+ return solidHandle(result);
894
+ }
895
+ cut(shape2, tool, _options) {
896
+ if (_options && hasBooleanOptions(_options)) {
897
+ warnOnce(
898
+ "boolean-options",
899
+ "BooleanOptions (optimisation, simplify, strategy, fuzzyValue) not supported; ignored."
900
+ );
901
+ }
902
+ const baseId = unwrapSolidOrThrow(shape2, "cut");
903
+ const toolHandle = tool;
904
+ if (toolHandle.type === "compound") {
905
+ const toolSolidIds = toArray(this.bk.getCompoundSolids(toolHandle.id));
906
+ let currentId = baseId;
907
+ for (const toolSolidId of toolSolidIds) {
908
+ currentId = this.bk.cut(currentId, toolSolidId);
909
+ }
910
+ return solidHandle(currentId);
911
+ }
912
+ const result = this.bk.cut(baseId, unwrapSolidOrThrow(tool, "cut"));
913
+ return solidHandle(result);
914
+ }
915
+ intersect(shape2, tool, _options) {
916
+ if (_options && hasBooleanOptions(_options)) {
917
+ warnOnce(
918
+ "boolean-options",
919
+ "BooleanOptions (optimisation, simplify, strategy, fuzzyValue) not supported; ignored."
920
+ );
921
+ }
922
+ const result = this.bk.intersect(
923
+ unwrapSolidOrThrow(shape2, "intersect"),
924
+ unwrapSolidOrThrow(tool, "intersect")
925
+ );
926
+ return solidHandle(result);
927
+ }
928
+ section(shape2, plane, _approximation) {
929
+ const { point, normal } = this.extractPlaneFromFace(plane);
930
+ const solidId = isBrepkitHandle(shape2) && shape2.type === "solid" ? shape2.id : unwrap(shape2, "solid");
931
+ const faceIds = toArray(
932
+ this.bk.section(solidId, point[0], point[1], point[2], normal[0], normal[1], normal[2])
933
+ );
934
+ if (faceIds.length === 0) {
935
+ return compoundHandle(this.bk.makeCompound([]));
936
+ }
937
+ const firstWireId = this.bk.getFaceOuterWire(faceIds[0]);
938
+ return wireHandle(firstWireId);
939
+ }
940
+ fuseAll(shapes, options) {
941
+ if (shapes.length === 0) throw new Error("brepkit: fuseAll requires at least one shape");
942
+ if (shapes.length === 1) return shapes[0];
943
+ let current = [...shapes];
944
+ while (current.length > 1) {
945
+ const next = [];
946
+ for (let i7 = 0; i7 < current.length; i7 += 2) {
947
+ if (i7 + 1 < current.length) {
948
+ next.push(this.fuse(current[i7], current[i7 + 1], options));
949
+ } else {
950
+ next.push(current[i7]);
951
+ }
952
+ }
953
+ current = next;
954
+ }
955
+ return current[0];
956
+ }
957
+ cutAll(shape2, tools, options) {
958
+ let result = shape2;
959
+ for (const tool of tools) {
960
+ result = this.cut(result, tool, options);
961
+ }
962
+ return result;
963
+ }
964
+ split(shape2, tools) {
965
+ if (tools.length === 0) throw new Error("brepkit: split requires at least one tool");
966
+ const { point, normal } = this.extractPlaneFromFace(tools[0]);
967
+ const result = toArray(
968
+ this.bk.split(
969
+ unwrap(shape2, "solid"),
970
+ point[0],
971
+ point[1],
972
+ point[2],
973
+ normal[0],
974
+ normal[1],
975
+ normal[2]
976
+ )
977
+ );
978
+ return compoundHandle(this.bk.makeCompound(result));
979
+ }
980
+ // ═══════════════════════════════════════════════════════════════════════
981
+ // Convex hull (not yet implemented)
982
+ // ═══════════════════════════════════════════════════════════════════════
983
+ hull(shapes, _tolerance) {
984
+ const coords = [];
985
+ for (const shape2 of shapes) {
986
+ const h8 = shape2;
987
+ if (h8.type === "solid") {
988
+ const vertIds = toArray(this.bk.getSolidVertices(h8.id));
989
+ for (const vid of vertIds) {
990
+ const pos = this.bk.getVertexPosition(vid);
991
+ coords.push(pos[0], pos[1], pos[2]);
992
+ }
993
+ } else if (h8.type === "vertex") {
994
+ const pos = this.bk.getVertexPosition(h8.id);
995
+ coords.push(pos[0], pos[1], pos[2]);
996
+ }
997
+ }
998
+ if (coords.length < 12) throw new Error("brepkit: hull requires enough points");
999
+ const id = this.bk.convexHull(coords);
1000
+ return solidHandle(id);
1001
+ }
1002
+ hullFromPoints(points, _tolerance) {
1003
+ if (points.length < 4) throw new Error("brepkit: hull needs at least 4 points");
1004
+ const coords = [];
1005
+ for (const p7 of points) {
1006
+ coords.push(p7.x, p7.y, p7.z);
1007
+ }
1008
+ const id = this.bk.convexHull(coords);
1009
+ return solidHandle(id);
1010
+ }
1011
+ buildSolidFromFaces(points, faces, _tolerance) {
1012
+ const positions = new Float64Array(points.length * 3);
1013
+ for (let i7 = 0; i7 < points.length; i7++) {
1014
+ const p7 = points[i7];
1015
+ positions[i7 * 3] = p7.x;
1016
+ positions[i7 * 3 + 1] = p7.y;
1017
+ positions[i7 * 3 + 2] = p7.z;
1018
+ }
1019
+ const indices = new Uint32Array(faces.length * 3);
1020
+ for (let i7 = 0; i7 < faces.length; i7++) {
1021
+ const f12 = faces[i7];
1022
+ indices[i7 * 3] = f12[0];
1023
+ indices[i7 * 3 + 1] = f12[1];
1024
+ indices[i7 * 3 + 2] = f12[2];
1025
+ }
1026
+ const id = this.bk.importIndexedMesh(positions, indices);
1027
+ return solidHandle(id);
1028
+ }
1029
+ // ═══════════════════════════════════════════════════════════════════════
1030
+ // Shape construction
1031
+ // ═══════════════════════════════════════════════════════════════════════
1032
+ makeVertex(x7, y6, z6) {
1033
+ const id = this.bk.makeVertex(x7, y6, z6);
1034
+ return vertexHandle(id);
1035
+ }
1036
+ makeEdge(curve, start, end) {
1037
+ if (curve && typeof curve === "object" && "origin" in curve && "direction" in curve) {
1038
+ const { origin, direction } = curve;
1039
+ const t0 = start ?? 0;
1040
+ const t1 = end ?? 1;
1041
+ return this.makeLineEdge(
1042
+ [
1043
+ origin[0] + direction[0] * t0,
1044
+ origin[1] + direction[1] * t0,
1045
+ origin[2] + direction[2] * t0
1046
+ ],
1047
+ [
1048
+ origin[0] + direction[0] * t1,
1049
+ origin[1] + direction[1] * t1,
1050
+ origin[2] + direction[2] * t1
1051
+ ]
1052
+ );
1053
+ }
1054
+ if (isBrepkitHandle(curve) && curve.type === "edge") {
1055
+ return curve;
1056
+ }
1057
+ throw new Error("brepkit: makeEdge requires a curve with origin/direction, or an edge handle");
1058
+ }
1059
+ makeWire(edges) {
1060
+ const edgeIds = [];
1061
+ for (const e8 of edges) {
1062
+ const h8 = e8;
1063
+ if (h8.type === "wire") {
1064
+ for (const childEdgeId of toArray(this.bk.getWireEdges(h8.id))) {
1065
+ edgeIds.push(childEdgeId);
1066
+ }
1067
+ } else {
1068
+ edgeIds.push(unwrap(e8, "edge"));
1069
+ }
1070
+ }
1071
+ const id = this.bk.makeWire(edgeIds, true);
1072
+ return wireHandle(id);
1073
+ }
1074
+ makeFace(wire, _planar) {
1075
+ const h8 = wire;
1076
+ if (h8.type === "edge") {
1077
+ const wireId = this.bk.makeWire([h8.id], true);
1078
+ const id2 = this.bk.makeFaceFromWire(wireId);
1079
+ return faceHandle(id2);
1080
+ }
1081
+ const id = this.bk.makeFaceFromWire(unwrap(wire, "wire"));
1082
+ return faceHandle(id);
1083
+ }
1084
+ makeBox(width, height, depth) {
1085
+ const id = this.bk.makeBox(width, height, depth);
1086
+ return solidHandle(id);
1087
+ }
1088
+ makeRectangle(width, height) {
1089
+ const id = this.bk.makeRectangle(width, height);
1090
+ return faceHandle(id);
1091
+ }
1092
+ makeCylinder(radius, height, center, direction) {
1093
+ const id = this.bk.makeCylinder(radius, height);
1094
+ const sh = solidHandle(id);
1095
+ if (this.needsTransform(center, direction)) {
1096
+ return this.transformToPlacement(sh, center, direction);
1097
+ }
1098
+ return sh;
1099
+ }
1100
+ makeSphere(radius, center) {
1101
+ const id = this.bk.makeSphere(radius, DEFAULT_SEGMENTS);
1102
+ const sh = solidHandle(id);
1103
+ if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
1104
+ return this.translate(sh, center[0], center[1], center[2]);
1105
+ }
1106
+ return sh;
1107
+ }
1108
+ makeCone(radius1, radius2, height, center, direction) {
1109
+ const id = this.bk.makeCone(radius1, radius2, height);
1110
+ const sh = solidHandle(id);
1111
+ if (this.needsTransform(center, direction)) {
1112
+ return this.transformToPlacement(sh, center, direction);
1113
+ }
1114
+ return sh;
1115
+ }
1116
+ makeTorus(majorRadius, minorRadius, center, direction) {
1117
+ const id = this.bk.makeTorus(majorRadius, minorRadius, DEFAULT_SEGMENTS);
1118
+ const sh = solidHandle(id);
1119
+ if (this.needsTransform(center, direction)) {
1120
+ return this.transformToPlacement(sh, center, direction);
1121
+ }
1122
+ return sh;
1123
+ }
1124
+ makeEllipsoid(aLength, bLength, cLength) {
1125
+ const maxR = Math.max(aLength, bLength, cLength);
1126
+ const sphere = this.makeSphere(maxR);
1127
+ const scaleX = aLength / maxR;
1128
+ const scaleY = bLength / maxR;
1129
+ const scaleZ = cLength / maxR;
1130
+ return this.generalTransform(
1131
+ sphere,
1132
+ [scaleX, 0, 0, 0, scaleY, 0, 0, 0, scaleZ],
1133
+ [0, 0, 0],
1134
+ false
1135
+ );
1136
+ }
1137
+ // --- Extended construction ---
1138
+ makeLineEdge(p1, p22) {
1139
+ const id = this.bk.makeLineEdge(p1[0], p1[1], p1[2], p22[0], p22[1], p22[2]);
1140
+ return edgeHandle(id);
1141
+ }
1142
+ makeCircleEdge(center, normal, radius) {
1143
+ return this.makeCircleNurbs(center, normal, radius, 0, 2 * Math.PI);
1144
+ }
1145
+ makeCircleArc(center, normal, radius, startAngle, endAngle) {
1146
+ return this.makeCircleNurbs(center, normal, radius, startAngle, endAngle);
1147
+ }
1148
+ makeArcEdge(p1, p22, p32) {
1149
+ const ab = [p22[0] - p1[0], p22[1] - p1[1], p22[2] - p1[2]];
1150
+ const ac = [p32[0] - p1[0], p32[1] - p1[1], p32[2] - p1[2]];
1151
+ const normal = [
1152
+ ab[1] * ac[2] - ab[2] * ac[1],
1153
+ ab[2] * ac[0] - ab[0] * ac[2],
1154
+ ab[0] * ac[1] - ab[1] * ac[0]
1155
+ ];
1156
+ const nLen = Math.sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2);
1157
+ if (nLen < 1e-12) {
1158
+ return this.makeLineEdge(p1, p32);
1159
+ }
1160
+ const nz = [normal[0] / nLen, normal[1] / nLen, normal[2] / nLen];
1161
+ const abLen = Math.sqrt(ab[0] ** 2 + ab[1] ** 2 + ab[2] ** 2);
1162
+ const ux = [ab[0] / abLen, ab[1] / abLen, ab[2] / abLen];
1163
+ const uy = [
1164
+ nz[1] * ux[2] - nz[2] * ux[1],
1165
+ nz[2] * ux[0] - nz[0] * ux[2],
1166
+ nz[0] * ux[1] - nz[1] * ux[0]
1167
+ ];
1168
+ const proj = (p7) => {
1169
+ const dx = p7[0] - p1[0], dy = p7[1] - p1[1], dz = p7[2] - p1[2];
1170
+ return [dx * ux[0] + dy * ux[1] + dz * ux[2], dx * uy[0] + dy * uy[1] + dz * uy[2]];
1171
+ };
1172
+ const [ax2, ay2] = proj(p1);
1173
+ const [bx2, by2] = proj(p22);
1174
+ const [cx2, cy2] = proj(p32);
1175
+ const d10 = 2 * (ax2 * (by2 - cy2) + bx2 * (cy2 - ay2) + cx2 * (ay2 - by2));
1176
+ if (Math.abs(d10) < 1e-12) {
1177
+ return this.makeLineEdge(p1, p32);
1178
+ }
1179
+ const ccx = ((ax2 ** 2 + ay2 ** 2) * (by2 - cy2) + (bx2 ** 2 + by2 ** 2) * (cy2 - ay2) + (cx2 ** 2 + cy2 ** 2) * (ay2 - by2)) / d10;
1180
+ const ccy = ((ax2 ** 2 + ay2 ** 2) * (cx2 - bx2) + (bx2 ** 2 + by2 ** 2) * (ax2 - cx2) + (cx2 ** 2 + cy2 ** 2) * (bx2 - ax2)) / d10;
1181
+ const center = [
1182
+ p1[0] + ccx * ux[0] + ccy * uy[0],
1183
+ p1[1] + ccx * ux[1] + ccy * uy[1],
1184
+ p1[2] + ccx * ux[2] + ccy * uy[2]
1185
+ ];
1186
+ const radius = Math.sqrt(
1187
+ (p1[0] - center[0]) ** 2 + (p1[1] - center[1]) ** 2 + (p1[2] - center[2]) ** 2
1188
+ );
1189
+ const lx = [p1[0] - center[0], p1[1] - center[1], p1[2] - center[2]];
1190
+ const lxLen = Math.sqrt(lx[0] ** 2 + lx[1] ** 2 + lx[2] ** 2);
1191
+ const uxA = [lx[0] / lxLen, lx[1] / lxLen, lx[2] / lxLen];
1192
+ const uyA = [
1193
+ nz[1] * uxA[2] - nz[2] * uxA[1],
1194
+ nz[2] * uxA[0] - nz[0] * uxA[2],
1195
+ nz[0] * uxA[1] - nz[1] * uxA[0]
1196
+ ];
1197
+ const v32 = [p32[0] - center[0], p32[1] - center[1], p32[2] - center[2]];
1198
+ const dotX = v32[0] * uxA[0] + v32[1] * uxA[1] + v32[2] * uxA[2];
1199
+ const dotY = v32[0] * uyA[0] + v32[1] * uyA[1] + v32[2] * uyA[2];
1200
+ let endAngle = Math.atan2(dotY, dotX);
1201
+ if (endAngle <= 0) endAngle += 2 * Math.PI;
1202
+ return this.makeCircleNurbs(center, normal, radius, 0, endAngle);
1203
+ }
1204
+ makeEllipseEdge(center, normal, majorRadius, minorRadius, xDir) {
1205
+ return this.makeEllipseNurbs(center, normal, majorRadius, minorRadius, 0, 2 * Math.PI, xDir);
1206
+ }
1207
+ makeEllipseArc(center, normal, majorRadius, minorRadius, startAngle, endAngle, xDir) {
1208
+ return this.makeEllipseNurbs(
1209
+ center,
1210
+ normal,
1211
+ majorRadius,
1212
+ minorRadius,
1213
+ startAngle,
1214
+ endAngle,
1215
+ xDir
1216
+ );
1217
+ }
1218
+ makeBezierEdge(points) {
1219
+ if (points.length < 2) throw new Error("brepkit: bezier requires at least 2 points");
1220
+ const degree = points.length - 1;
1221
+ const n9 = points.length;
1222
+ const knots = [...Array(degree + 1).fill(0), ...Array(degree + 1).fill(1)];
1223
+ const weights = Array(n9).fill(1);
1224
+ const flatCp = points.flatMap(([x7, y6, z6]) => [x7, y6, z6]);
1225
+ const startPt = points[0];
1226
+ const endPt = points[n9 - 1];
1227
+ const id = this.bk.makeNurbsEdge(
1228
+ startPt[0],
1229
+ startPt[1],
1230
+ startPt[2],
1231
+ endPt[0],
1232
+ endPt[1],
1233
+ endPt[2],
1234
+ degree,
1235
+ knots,
1236
+ flatCp,
1237
+ weights
1238
+ );
1239
+ return edgeHandle(id);
1240
+ }
1241
+ makeTangentArc(startPoint, startTangent, endPoint) {
1242
+ const cp1 = [
1243
+ startPoint[0] + startTangent[0] / 3,
1244
+ startPoint[1] + startTangent[1] / 3,
1245
+ startPoint[2] + startTangent[2] / 3
1246
+ ];
1247
+ const dx = endPoint[0] - cp1[0];
1248
+ const dy = endPoint[1] - cp1[1];
1249
+ const dz = endPoint[2] - cp1[2];
1250
+ const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
1251
+ const cp2 = [
1252
+ endPoint[0] - dx / (3 * Math.max(len, 1e-10)),
1253
+ endPoint[1] - dy / (3 * Math.max(len, 1e-10)),
1254
+ endPoint[2] - dz / (3 * Math.max(len, 1e-10))
1255
+ ];
1256
+ return this.makeBezierEdge([startPoint, cp1, cp2, endPoint]);
1257
+ }
1258
+ makeHelixWire(pitch, height, radius, center, _direction, leftHanded) {
1259
+ const turns = height / pitch;
1260
+ const nSamplesPerTurn = 16;
1261
+ const nSamples = Math.max(4, Math.ceil(turns * nSamplesPerTurn));
1262
+ const cx = center?.[0] ?? 0;
1263
+ const cy = center?.[1] ?? 0;
1264
+ const cz = center?.[2] ?? 0;
1265
+ const sign = leftHanded ? -1 : 1;
1266
+ const points = [];
1267
+ for (let i7 = 0; i7 <= nSamples; i7++) {
1268
+ const t11 = i7 / nSamples;
1269
+ const angle = sign * 2 * Math.PI * turns * t11;
1270
+ points.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle), cz + height * t11]);
1271
+ }
1272
+ const edge = this.interpolatePoints(points);
1273
+ return this.makeWire([edge]);
1274
+ }
1275
+ makeWireFromMixed(items) {
1276
+ const edgeIds = [];
1277
+ for (const item of items) {
1278
+ const h8 = item;
1279
+ if (h8.type === "edge") {
1280
+ edgeIds.push(h8.id);
1281
+ } else if (h8.type === "wire") {
1282
+ for (const childEdgeId of toArray(this.bk.getWireEdges(h8.id))) {
1283
+ edgeIds.push(childEdgeId);
1284
+ }
1285
+ }
1286
+ }
1287
+ if (edgeIds.length === 0)
1288
+ throw new Error("brepkit: makeWireFromMixed requires at least one edge");
1289
+ const id = this.bk.makeWire(edgeIds, false);
1290
+ return wireHandle(id);
1291
+ }
1292
+ makeCompound(shapes) {
1293
+ const handles = shapes.filter(isBrepkitHandle);
1294
+ if (handles.length === 0) {
1295
+ throw new Error("brepkit: makeCompound requires at least one shape");
1296
+ }
1297
+ const allSolids = handles.every((h8) => h8.type === "solid");
1298
+ if (allSolids) {
1299
+ const id2 = this.bk.makeCompound(handles.map((h8) => h8.id));
1300
+ return compoundHandle(id2);
1301
+ }
1302
+ const id = syntheticCompoundCounter++;
1303
+ syntheticCompounds.set(id, handles);
1304
+ return compoundHandle(id);
1305
+ }
1306
+ makeBoxFromCorners(p1, p22) {
1307
+ const w6 = Math.abs(p22[0] - p1[0]);
1308
+ const h8 = Math.abs(p22[1] - p1[1]);
1309
+ const d10 = Math.abs(p22[2] - p1[2]);
1310
+ const box = this.makeBox(w6, h8, d10);
1311
+ const minX = Math.min(p1[0], p22[0]);
1312
+ const minY = Math.min(p1[1], p22[1]);
1313
+ const minZ = Math.min(p1[2], p22[2]);
1314
+ if (minX !== 0 || minY !== 0 || minZ !== 0) {
1315
+ return this.translate(box, minX, minY, minZ);
1316
+ }
1317
+ return box;
1318
+ }
1319
+ solidFromShell(shell2) {
1320
+ const h8 = shell2;
1321
+ if (h8.type === "solid") return shell2;
1322
+ if (h8.type === "shell") {
1323
+ try {
1324
+ this.bk.getSolidFaces(h8.id);
1325
+ return solidHandle(h8.id);
1326
+ } catch {
1327
+ }
1328
+ const id2 = this.bk.solidFromShell(h8.id);
1329
+ return solidHandle(id2);
1330
+ }
1331
+ const id = this.bk.solidFromShell(unwrap(shell2, "shell"));
1332
+ return solidHandle(id);
1333
+ }
1334
+ // ═══════════════════════════════════════════════════════════════════════
1335
+ // Extrusion / sweep / loft / revolution
1336
+ // ═══════════════════════════════════════════════════════════════════════
1337
+ extrude(face, direction, length) {
1338
+ const id = this.bk.extrude(
1339
+ unwrap(face, "face"),
1340
+ direction[0],
1341
+ direction[1],
1342
+ direction[2],
1343
+ length
1344
+ );
1345
+ return solidHandle(id);
1346
+ }
1347
+ revolve(shape2, axis, angle) {
1348
+ if (axis && typeof axis === "object" && "origin" in axis && "direction" in axis) {
1349
+ const { origin, direction } = axis;
1350
+ let angleDeg = angle * (180 / Math.PI);
1351
+ if (angleDeg > 360) angleDeg = 360;
1352
+ const id = this.bk.revolve(
1353
+ unwrap(shape2, "face"),
1354
+ origin[0],
1355
+ origin[1],
1356
+ origin[2],
1357
+ direction[0],
1358
+ direction[1],
1359
+ direction[2],
1360
+ angleDeg
1361
+ );
1362
+ return solidHandle(id);
1363
+ }
1364
+ throw new Error("brepkit: revolve requires axis with origin and direction");
1365
+ }
1366
+ revolveVec(shape2, center, direction, angle) {
1367
+ let angleDeg = angle * (180 / Math.PI);
1368
+ if (angleDeg > 360) angleDeg = 360;
1369
+ const id = this.bk.revolve(
1370
+ unwrap(shape2, "face"),
1371
+ center[0],
1372
+ center[1],
1373
+ center[2],
1374
+ direction[0],
1375
+ direction[1],
1376
+ direction[2],
1377
+ angleDeg
1378
+ );
1379
+ return solidHandle(id);
1380
+ }
1381
+ loft(wires, _ruled, _startShape, _endShape) {
1382
+ if (_ruled !== void 0 || _startShape !== void 0 || _endShape !== void 0) {
1383
+ warnOnce(
1384
+ "loft-options",
1385
+ "Loft options (ruled, startShape, endShape) not supported; ignored."
1386
+ );
1387
+ }
1388
+ const faceIds = wires.map((w6) => {
1389
+ const h8 = w6;
1390
+ if (h8.type === "wire") {
1391
+ return this.bk.makeFaceFromWire(h8.id);
1392
+ }
1393
+ return unwrap(w6, "face");
1394
+ });
1395
+ const id = this.bk.loft(faceIds);
1396
+ return solidHandle(id);
1397
+ }
1398
+ sweep(wire, spine, _options) {
1399
+ if (_options?.transitionMode !== void 0) {
1400
+ warnOnce("sweep-transition", "Sweep transition mode not supported; ignored.");
1401
+ }
1402
+ const spineHandle = spine;
1403
+ if (spineHandle.type === "wire") {
1404
+ const edges = this.iterShapes(spine, "edge");
1405
+ const edgeIds = edges.map((e8) => unwrap(e8, "edge"));
1406
+ const id2 = this.bk.sweepAlongEdges(unwrap(wire, "face"), edgeIds);
1407
+ return solidHandle(id2);
1408
+ }
1409
+ const nurbsData = this.extractNurbsFromEdge(spine);
1410
+ if (!nurbsData) {
1411
+ throw new Error("brepkit: sweep spine must be an edge or wire");
1412
+ }
1413
+ const id = this.bk.sweep(
1414
+ unwrap(wire, "face"),
1415
+ nurbsData.degree,
1416
+ nurbsData.knots,
1417
+ nurbsData.controlPoints,
1418
+ nurbsData.weights
1419
+ );
1420
+ return solidHandle(id);
1421
+ }
1422
+ simplePipe(profile, spine) {
1423
+ const profileHandle = profile;
1424
+ const faceId = profileHandle.type === "wire" ? this.bk.makeFaceFromWire(profileHandle.id) : unwrap(profile, "face");
1425
+ const spineHandle = spine;
1426
+ if (spineHandle.type === "wire") {
1427
+ const edges = this.iterShapes(spine, "edge");
1428
+ const edgeIds = edges.map((e8) => unwrap(e8, "edge"));
1429
+ const id2 = this.bk.sweepAlongEdges(faceId, edgeIds);
1430
+ return solidHandle(id2);
1431
+ }
1432
+ const nurbsData = this.extractNurbsFromEdge(spine);
1433
+ if (!nurbsData) {
1434
+ throw new Error("brepkit: pipe spine must be an edge or wire");
1435
+ }
1436
+ const id = this.bk.pipe(
1437
+ faceId,
1438
+ nurbsData.degree,
1439
+ nurbsData.knots,
1440
+ nurbsData.controlPoints,
1441
+ nurbsData.weights
1442
+ );
1443
+ return solidHandle(id);
1444
+ }
1445
+ // ═══════════════════════════════════════════════════════════════════════
1446
+ // Modification
1447
+ // ═══════════════════════════════════════════════════════════════════════
1448
+ fillet(shape2, edges, radius) {
1449
+ const r9 = typeof radius === "number" ? radius : Array.isArray(radius) ? radius[0] : 1;
1450
+ if (typeof radius !== "number") {
1451
+ warnOnce(
1452
+ "fillet-variable",
1453
+ typeof radius === "function" ? "Per-edge fillet radius function not supported; falling back to radius=1." : "Variable-radius fillet not supported; using first radius only."
1454
+ );
1455
+ }
1456
+ const edgeIds = edges.map((e8) => unwrap(e8, "edge"));
1457
+ const id = this.bk.fillet(unwrapSolidOrThrow(shape2, "fillet"), edgeIds, r9);
1458
+ return solidHandle(id);
1459
+ }
1460
+ chamfer(shape2, edges, distance) {
1461
+ const d10 = typeof distance === "number" ? distance : Array.isArray(distance) ? distance[0] : 1;
1462
+ if (typeof distance !== "number") {
1463
+ warnOnce(
1464
+ "chamfer-asymmetric",
1465
+ typeof distance === "function" ? "Per-edge chamfer distance function not supported; falling back to distance=1." : "Asymmetric chamfer not supported; using first distance only."
1466
+ );
1467
+ }
1468
+ const edgeIds = edges.map((e8) => unwrap(e8, "edge"));
1469
+ const id = this.bk.chamfer(unwrapSolidOrThrow(shape2, "chamfer"), edgeIds, d10);
1470
+ return solidHandle(id);
1471
+ }
1472
+ chamferDistAngle(shape2, edges, distance, angleDeg) {
1473
+ warnOnce("chamfer-dist-angle", "Distance-angle chamfer approximated as uniform chamfer.");
1474
+ const d22 = distance * Math.tan(angleDeg * Math.PI / 180);
1475
+ const avgDist = (distance + d22) / 2;
1476
+ return this.chamfer(shape2, edges, avgDist);
1477
+ }
1478
+ shell(shape2, faces, thickness, tolerance) {
1479
+ if (tolerance !== void 0) {
1480
+ warnOnce(
1481
+ "shell-tolerance",
1482
+ "shell() tolerance parameter is not supported; brepkit uses its own internal tolerance."
1483
+ );
1484
+ }
1485
+ const solidId = unwrapSolidOrThrow(shape2, "shell");
1486
+ const solidFaces = toArray(this.bk.getSolidFaces(solidId));
1487
+ const solidFaceSet = new Set(solidFaces);
1488
+ const resolvedFaceIds = faces.map((f12) => {
1489
+ const fid = unwrap(f12, "face");
1490
+ if (solidFaceSet.has(fid)) return fid;
1491
+ try {
1492
+ const origNormal = this.bk.getFaceNormal(fid);
1493
+ let bestMatch = -1;
1494
+ let bestDot = -2;
1495
+ for (const sf of solidFaces) {
1496
+ try {
1497
+ const sn = this.bk.getFaceNormal(sf);
1498
+ const dot = (origNormal[0] ?? 0) * (sn[0] ?? 0) + (origNormal[1] ?? 0) * (sn[1] ?? 0) + (origNormal[2] ?? 0) * (sn[2] ?? 0);
1499
+ if (dot > bestDot) {
1500
+ bestDot = dot;
1501
+ bestMatch = sf;
1502
+ }
1503
+ } catch {
1504
+ }
1505
+ }
1506
+ if (bestMatch >= 0 && bestDot > 0.99) return bestMatch;
1507
+ } catch {
1508
+ }
1509
+ return fid;
1510
+ });
1511
+ const id = this.bk.shell(solidId, thickness, resolvedFaceIds);
1512
+ return solidHandle(id);
1513
+ }
1514
+ thicken(shape2, thickness) {
1515
+ const h8 = shape2;
1516
+ if (h8.type === "face") {
1517
+ const id = this.bk.thicken(h8.id, thickness);
1518
+ return solidHandle(id);
1519
+ }
1520
+ throw new Error("brepkit: thicken() requires a face");
1521
+ }
1522
+ offset(shape2, distance, tolerance) {
1523
+ if (tolerance !== void 0) {
1524
+ warnOnce(
1525
+ "offset-tolerance",
1526
+ "offset() tolerance parameter is not supported; brepkit uses its own internal tolerance."
1527
+ );
1528
+ }
1529
+ const h8 = shape2;
1530
+ if (h8.type === "face") {
1531
+ const id2 = this.bk.thicken(h8.id, distance);
1532
+ return solidHandle(id2);
1533
+ }
1534
+ const id = this.bk.offsetSolid(unwrapSolidOrThrow(shape2, "offset"), distance);
1535
+ return solidHandle(id);
1536
+ }
1537
+ // ═══════════════════════════════════════════════════════════════════════
1538
+ // Transforms
1539
+ // ═══════════════════════════════════════════════════════════════════════
1540
+ transform(shape2, trsf) {
1541
+ if (Array.isArray(trsf) && trsf.length === 16) {
1542
+ return this.applyMatrix(shape2, trsf);
1543
+ }
1544
+ throw new Error("brepkit: transform expects a 16-element matrix array");
1545
+ }
1546
+ translate(shape2, x7, y6, z6) {
1547
+ return this.applyMatrix(shape2, translationMatrix(x7, y6, z6));
1548
+ }
1549
+ rotate(shape2, angle, axis, center) {
1550
+ return this.applyMatrix(shape2, rotationMatrix(angle, axis, center));
1551
+ }
1552
+ mirror(shape2, origin, normal) {
1553
+ const h8 = shape2;
1554
+ if (h8.type === "solid") {
1555
+ const id = this.bk.mirror(
1556
+ h8.id,
1557
+ origin[0],
1558
+ origin[1],
1559
+ origin[2],
1560
+ normal[0],
1561
+ normal[1],
1562
+ normal[2]
1563
+ );
1564
+ return solidHandle(id);
1565
+ }
1566
+ return this.applyMatrix(shape2, mirrorMatrix(origin, normal));
1567
+ }
1568
+ scale(shape2, center, factor) {
1569
+ return this.applyMatrix(shape2, scaleMatrix(center, factor));
1570
+ }
1571
+ generalTransform(shape2, linear, translation, _isOrthogonal) {
1572
+ return this.applyMatrix(shape2, affineMatrix(linear, translation));
1573
+ }
1574
+ generalTransformNonOrthogonal(shape2, linear, translation) {
1575
+ return this.applyMatrix(shape2, affineMatrix(linear, translation));
1576
+ }
1577
+ // ═══════════════════════════════════════════════════════════════════════
1578
+ // Operations with shape evolution tracking
1579
+ // ═══════════════════════════════════════════════════════════════════════
1580
+ /**
1581
+ * Parse native brepkit evolution JSON and convert face IDs to hash-based
1582
+ * evolution that the brepjs propagation system expects.
1583
+ *
1584
+ * The native API returns:
1585
+ * `{"solid": u32, "evolution": {"modified": {inputFaceId: [outputFaceIds]}, "generated": {}, "deleted": [faceIds]}}`
1586
+ *
1587
+ * We convert face IDs → hashes via `id % hashUpperBound`.
1588
+ */
1589
+ parseNativeEvolution(json, hashUpperBound) {
1590
+ const parsed = JSON.parse(json);
1591
+ const evo = parsed.evolution;
1592
+ if (!evo || typeof evo.modified !== "object" || typeof evo.generated !== "object") {
1593
+ throw new Error("brepkit: invalid evolution JSON structure");
1594
+ }
1595
+ const resultShape = solidHandle(parsed.solid);
1596
+ const collectHashes = (entries) => {
1597
+ const map = /* @__PURE__ */ new Map();
1598
+ for (const [inputId, outputIds] of Object.entries(entries)) {
1599
+ const inputHash = Number(inputId) % hashUpperBound;
1600
+ const outputHashes = outputIds.map((id) => id % hashUpperBound);
1601
+ const existing = map.get(inputHash);
1602
+ if (existing) {
1603
+ existing.push(...outputHashes);
1604
+ } else {
1605
+ map.set(inputHash, outputHashes);
1606
+ }
1607
+ }
1608
+ return map;
1609
+ };
1610
+ const modified = collectHashes(evo.modified);
1611
+ const generated = collectHashes(evo.generated);
1612
+ const deleted = /* @__PURE__ */ new Set();
1613
+ for (const id of evo.deleted) {
1614
+ deleted.add(id % hashUpperBound);
1615
+ }
1616
+ return { shape: resultShape, evolution: { modified, generated, deleted } };
1617
+ }
1618
+ /**
1619
+ * Build a ShapeEvolution by comparing input face hashes to output face hashes.
1620
+ *
1621
+ * For transforms: 1:1 mapping (modified = identity, no generated/deleted).
1622
+ * For booleans/modifiers: compare sets to detect changes, with geometric
1623
+ * fallback when hash matching fails (brepkit always creates new face IDs).
1624
+ */
1625
+ buildEvolution(resultShape, inputFaceHashes, hashUpperBound, isTransform, originalShape) {
1626
+ const h8 = resultShape;
1627
+ const modified = /* @__PURE__ */ new Map();
1628
+ const generated = /* @__PURE__ */ new Map();
1629
+ const deleted = /* @__PURE__ */ new Set();
1630
+ if (h8.type === "solid") {
1631
+ const outputFaces = toArray(this.bk.getSolidFaces(h8.id));
1632
+ const outputHashes = outputFaces.map((fid) => fid % hashUpperBound);
1633
+ if (isTransform) {
1634
+ for (let i7 = 0; i7 < inputFaceHashes.length && i7 < outputHashes.length; i7++) {
1635
+ modified.set(inputFaceHashes[i7], [outputHashes[i7]]);
1636
+ }
1637
+ } else {
1638
+ const inputSet = new Set(inputFaceHashes);
1639
+ let hasOverlap = false;
1640
+ for (const hash of outputHashes) {
1641
+ if (inputSet.has(hash)) {
1642
+ hasOverlap = true;
1643
+ break;
1644
+ }
1645
+ }
1646
+ if (hasOverlap) {
1647
+ const outputSet = new Set(outputHashes);
1648
+ for (const hash of outputHashes) {
1649
+ if (inputSet.has(hash)) {
1650
+ modified.set(hash, [hash]);
1651
+ }
1652
+ }
1653
+ const newFaces = outputHashes.filter((fh) => !inputSet.has(fh));
1654
+ if (newFaces.length > 0 && inputFaceHashes.length > 0) {
1655
+ generated.set(inputFaceHashes[0], newFaces);
1656
+ }
1657
+ for (const hash of inputFaceHashes) {
1658
+ if (!outputSet.has(hash)) {
1659
+ deleted.add(hash);
1660
+ }
1661
+ }
1662
+ } else if (originalShape) {
1663
+ this.matchFacesGeometrically(
1664
+ originalShape,
1665
+ inputFaceHashes,
1666
+ outputFaces,
1667
+ hashUpperBound,
1668
+ modified,
1669
+ generated,
1670
+ deleted
1671
+ );
1672
+ } else {
1673
+ for (let i7 = 0; i7 < inputFaceHashes.length && i7 < outputHashes.length; i7++) {
1674
+ modified.set(inputFaceHashes[i7], [outputHashes[i7]]);
1675
+ }
1676
+ if (outputHashes.length > inputFaceHashes.length && inputFaceHashes.length > 0) {
1677
+ generated.set(inputFaceHashes[0], outputHashes.slice(inputFaceHashes.length));
1678
+ }
1679
+ }
1680
+ }
1681
+ }
1682
+ return { shape: resultShape, evolution: { modified, generated, deleted } };
1683
+ }
1684
+ /**
1685
+ * Chain an evolution map (modified or generated) through one step of a multi-step
1686
+ * boolean. For each entry, each previous output hash is resolved against this
1687
+ * step's evolution: if it was further modified, follow to the new outputs; if
1688
+ * deleted, drop it; otherwise keep it unchanged.
1689
+ *
1690
+ * Mutates `map` in-place and records each resolved prevOut in `intermediateOutputs`.
1691
+ * When `deleteOnEmpty` is provided, entries that reduce to no outputs are added to it.
1692
+ */
1693
+ static chainEvolutionMap(map, stepModified, stepDeleted, intermediateOutputs, deleteOnEmpty) {
1694
+ for (const [origKey, prevOutputs] of map) {
1695
+ const chainedOutputs = [];
1696
+ for (const prevOut of prevOutputs) {
1697
+ intermediateOutputs.add(prevOut);
1698
+ const nextOutputs = stepModified.get(prevOut);
1699
+ if (nextOutputs) {
1700
+ chainedOutputs.push(...nextOutputs);
1701
+ } else if (!stepDeleted.has(prevOut)) {
1702
+ chainedOutputs.push(prevOut);
1703
+ }
1704
+ }
1705
+ if (chainedOutputs.length > 0) {
1706
+ map.set(origKey, chainedOutputs);
1707
+ } else {
1708
+ map.delete(origKey);
1709
+ deleteOnEmpty?.add(origKey);
1710
+ }
1711
+ }
1712
+ }
1713
+ /** Squared Euclidean distance between two 3-component centroids. */
1714
+ static centroidDistSq(a9, b10) {
1715
+ const dx = a9[0] - b10[0];
1716
+ const dy = a9[1] - b10[1];
1717
+ const dz = a9[2] - b10[2];
1718
+ return dx * dx + dy * dy + dz * dz;
1719
+ }
1720
+ /** Compute face centroid as the average of tessellation vertices. */
1721
+ faceCentroidById(faceId) {
1722
+ try {
1723
+ const pos = this.bk.tessellateFace(faceId, 1).positions;
1724
+ if (pos.length < 3) return [0, 0, 0];
1725
+ let cx = 0;
1726
+ let cy = 0;
1727
+ let cz = 0;
1728
+ const nVerts = pos.length / 3;
1729
+ for (let i7 = 0; i7 < pos.length; i7 += 3) {
1730
+ cx += pos[i7];
1731
+ cy += pos[i7 + 1];
1732
+ cz += pos[i7 + 2];
1733
+ }
1734
+ return [cx / nVerts, cy / nVerts, cz / nVerts];
1735
+ } catch {
1736
+ return [0, 0, 0];
1737
+ }
1738
+ }
1739
+ /**
1740
+ * Match input→output faces geometrically using normal dot product and centroid distance.
1741
+ * Mirrors the algorithm in brepkit's `boolean_with_evolution`.
1742
+ */
1743
+ matchFacesGeometrically(originalShape, inputFaceHashes, outputFaceIds, hashUpperBound, modified, generated, deleted) {
1744
+ const orig = originalShape;
1745
+ if (orig.type !== "solid") return;
1746
+ const inputFaceIds = toArray(this.bk.getSolidFaces(orig.id));
1747
+ const hashCount = Math.min(inputFaceIds.length, inputFaceHashes.length);
1748
+ const inputSigs = [];
1749
+ for (let i7 = 0; i7 < hashCount; i7++) {
1750
+ const fid = inputFaceIds[i7];
1751
+ try {
1752
+ const normal = this.bk.getFaceNormal(fid);
1753
+ const centroid = this.faceCentroidById(fid);
1754
+ inputSigs.push({ hash: inputFaceHashes[i7] ?? fid % hashUpperBound, normal, centroid });
1755
+ } catch {
1756
+ inputSigs.push({
1757
+ hash: inputFaceHashes[i7] ?? fid % hashUpperBound,
1758
+ normal: [0, 0, 0],
1759
+ centroid: this.faceCentroidById(fid)
1760
+ });
1761
+ }
1762
+ }
1763
+ const outputSigs = [];
1764
+ for (const fid of outputFaceIds) {
1765
+ try {
1766
+ const normal = this.bk.getFaceNormal(fid);
1767
+ const centroid = this.faceCentroidById(fid);
1768
+ outputSigs.push({ hash: fid % hashUpperBound, normal, centroid });
1769
+ } catch {
1770
+ outputSigs.push({
1771
+ hash: fid % hashUpperBound,
1772
+ normal: [0, 0, 0],
1773
+ centroid: this.faceCentroidById(fid)
1774
+ });
1775
+ }
1776
+ }
1777
+ const NORMAL_THRESHOLD = 0.707;
1778
+ const CENTROID_DIST_SQ_MAX = 100;
1779
+ const matchedInputIndices = /* @__PURE__ */ new Set();
1780
+ for (const out of outputSigs) {
1781
+ let bestScore = -Infinity;
1782
+ let bestIdx = -1;
1783
+ for (let i7 = 0; i7 < inputSigs.length; i7++) {
1784
+ const inp = inputSigs[i7];
1785
+ const dot = (out.normal[0] ?? 0) * (inp.normal[0] ?? 0) + (out.normal[1] ?? 0) * (inp.normal[1] ?? 0) + (out.normal[2] ?? 0) * (inp.normal[2] ?? 0);
1786
+ if (dot < NORMAL_THRESHOLD) continue;
1787
+ const distSq = BrepkitAdapter.centroidDistSq(out.centroid, inp.centroid);
1788
+ if (distSq > CENTROID_DIST_SQ_MAX) continue;
1789
+ const score = dot - distSq / CENTROID_DIST_SQ_MAX;
1790
+ if (score > bestScore) {
1791
+ bestScore = score;
1792
+ bestIdx = i7;
1793
+ }
1794
+ }
1795
+ if (bestIdx >= 0) {
1796
+ const bestInput = inputSigs[bestIdx];
1797
+ const existing = modified.get(bestInput.hash) ?? [];
1798
+ existing.push(out.hash);
1799
+ modified.set(bestInput.hash, existing);
1800
+ matchedInputIndices.add(bestIdx);
1801
+ } else {
1802
+ let bestDistSq = Infinity;
1803
+ let nearestInput;
1804
+ for (const inp of inputSigs) {
1805
+ const distSq = BrepkitAdapter.centroidDistSq(out.centroid, inp.centroid);
1806
+ if (distSq < bestDistSq) {
1807
+ bestDistSq = distSq;
1808
+ nearestInput = inp;
1809
+ }
1810
+ }
1811
+ if (nearestInput) {
1812
+ const existing = generated.get(nearestInput.hash) ?? [];
1813
+ existing.push(out.hash);
1814
+ generated.set(nearestInput.hash, existing);
1815
+ }
1816
+ }
1817
+ }
1818
+ for (let i7 = 0; i7 < inputSigs.length; i7++) {
1819
+ if (!matchedInputIndices.has(i7)) {
1820
+ deleted.add(inputSigs[i7].hash);
1821
+ }
1822
+ }
1823
+ }
1824
+ translateWithHistory(shape2, x7, y6, z6, inputFaceHashes, hashUpperBound) {
1825
+ return this.buildEvolution(
1826
+ this.translate(shape2, x7, y6, z6),
1827
+ inputFaceHashes,
1828
+ hashUpperBound,
1829
+ true
1830
+ );
1831
+ }
1832
+ rotateWithHistory(shape2, angle, inputFaceHashes, hashUpperBound, axis, center) {
1833
+ const angleDeg = angle * 180 / Math.PI;
1834
+ return this.buildEvolution(
1835
+ this.rotate(shape2, angleDeg, axis, center),
1836
+ inputFaceHashes,
1837
+ hashUpperBound,
1838
+ true
1839
+ );
1840
+ }
1841
+ mirrorWithHistory(shape2, origin, normal, inputFaceHashes, hashUpperBound) {
1842
+ return this.buildEvolution(
1843
+ this.mirror(shape2, origin, normal),
1844
+ inputFaceHashes,
1845
+ hashUpperBound,
1846
+ true
1847
+ );
1848
+ }
1849
+ scaleWithHistory(shape2, center, factor, inputFaceHashes, hashUpperBound) {
1850
+ return this.buildEvolution(
1851
+ this.scale(shape2, center, factor),
1852
+ inputFaceHashes,
1853
+ hashUpperBound,
1854
+ true
1855
+ );
1856
+ }
1857
+ generalTransformWithHistory(shape2, linear, translation, isOrthogonal, inputFaceHashes, hashUpperBound) {
1858
+ return this.buildEvolution(
1859
+ this.generalTransform(shape2, linear, translation, isOrthogonal),
1860
+ inputFaceHashes,
1861
+ hashUpperBound,
1862
+ true
1863
+ );
1864
+ }
1865
+ booleanWithHistoryImpl(shape2, tool, inputFaceHashes, hashUpperBound, options, nativeFn, fallbackFn, _label) {
1866
+ const sh = shape2;
1867
+ const th = tool;
1868
+ if (inputFaceHashes.length > 0 && sh.type === "solid") {
1869
+ if (th.type === "solid") {
1870
+ const json = nativeFn(sh.id, th.id);
1871
+ return this.parseNativeEvolution(json, hashUpperBound);
1872
+ }
1873
+ if (th.type === "compound") {
1874
+ const childSolidIds = toArray(this.bk.getCompoundSolids(th.id));
1875
+ let currentShape = shape2;
1876
+ const combinedModified = /* @__PURE__ */ new Map();
1877
+ const combinedGenerated = /* @__PURE__ */ new Map();
1878
+ const combinedDeleted = /* @__PURE__ */ new Set();
1879
+ const inputFaceHashSet = new Set(inputFaceHashes);
1880
+ for (const childId of childSolidIds) {
1881
+ const ch = currentShape;
1882
+ if (ch.type !== "solid") break;
1883
+ const json = nativeFn(ch.id, childId);
1884
+ const result = this.parseNativeEvolution(json, hashUpperBound);
1885
+ currentShape = result.shape;
1886
+ const intermediateOutputs = /* @__PURE__ */ new Set();
1887
+ BrepkitAdapter.chainEvolutionMap(
1888
+ combinedModified,
1889
+ result.evolution.modified,
1890
+ result.evolution.deleted,
1891
+ intermediateOutputs,
1892
+ combinedDeleted
1893
+ );
1894
+ BrepkitAdapter.chainEvolutionMap(
1895
+ combinedGenerated,
1896
+ result.evolution.modified,
1897
+ result.evolution.deleted,
1898
+ intermediateOutputs
1899
+ );
1900
+ for (const [k7, v9] of result.evolution.modified) {
1901
+ if (!combinedModified.has(k7) && !intermediateOutputs.has(k7)) {
1902
+ combinedModified.set(k7, [...v9]);
1903
+ }
1904
+ }
1905
+ for (const [k7, v9] of result.evolution.generated) {
1906
+ if (!intermediateOutputs.has(k7)) {
1907
+ const existing = combinedGenerated.get(k7) ?? [];
1908
+ combinedGenerated.set(k7, [...existing, ...v9]);
1909
+ }
1910
+ }
1911
+ for (const d10 of result.evolution.deleted) {
1912
+ if (inputFaceHashSet.has(d10)) {
1913
+ combinedDeleted.add(d10);
1914
+ }
1915
+ }
1916
+ }
1917
+ return {
1918
+ shape: currentShape,
1919
+ evolution: {
1920
+ modified: combinedModified,
1921
+ generated: combinedGenerated,
1922
+ deleted: combinedDeleted
1923
+ }
1924
+ };
1925
+ }
1926
+ }
1927
+ const fallbackResult = fallbackFn(shape2, tool, options);
1928
+ return this.buildEvolution(fallbackResult, inputFaceHashes, hashUpperBound, false, shape2);
1929
+ }
1930
+ fuseWithHistory(shape2, tool, inputFaceHashes, hashUpperBound, options) {
1931
+ return this.booleanWithHistoryImpl(
1932
+ shape2,
1933
+ tool,
1934
+ inputFaceHashes,
1935
+ hashUpperBound,
1936
+ options,
1937
+ (a9, b10) => this.bk.fuseWithEvolution(a9, b10),
1938
+ (s6, t11, o9) => this.fuse(s6, t11, o9),
1939
+ "fuseWithHistory"
1940
+ );
1941
+ }
1942
+ cutWithHistory(shape2, tool, inputFaceHashes, hashUpperBound, options) {
1943
+ return this.booleanWithHistoryImpl(
1944
+ shape2,
1945
+ tool,
1946
+ inputFaceHashes,
1947
+ hashUpperBound,
1948
+ options,
1949
+ (a9, b10) => this.bk.cutWithEvolution(a9, b10),
1950
+ (s6, t11, o9) => this.cut(s6, t11, o9),
1951
+ "cutWithHistory"
1952
+ );
1953
+ }
1954
+ intersectWithHistory(shape2, tool, inputFaceHashes, hashUpperBound, options) {
1955
+ return this.booleanWithHistoryImpl(
1956
+ shape2,
1957
+ tool,
1958
+ inputFaceHashes,
1959
+ hashUpperBound,
1960
+ options,
1961
+ (a9, b10) => this.bk.intersectWithEvolution(a9, b10),
1962
+ (s6, t11, o9) => this.intersect(s6, t11, o9),
1963
+ "intersectWithHistory"
1964
+ );
1965
+ }
1966
+ filletWithHistory(shape2, edges, radius, inputFaceHashes, hashUpperBound) {
1967
+ return this.buildEvolution(
1968
+ this.fillet(shape2, edges, radius),
1969
+ inputFaceHashes,
1970
+ hashUpperBound,
1971
+ false,
1972
+ shape2
1973
+ );
1974
+ }
1975
+ chamferWithHistory(shape2, edges, distance, inputFaceHashes, hashUpperBound) {
1976
+ return this.buildEvolution(
1977
+ this.chamfer(shape2, edges, distance),
1978
+ inputFaceHashes,
1979
+ hashUpperBound,
1980
+ false,
1981
+ shape2
1982
+ );
1983
+ }
1984
+ shellWithHistory(shape2, faces, thickness, inputFaceHashes, hashUpperBound, tolerance) {
1985
+ return this.buildEvolution(
1986
+ this.shell(shape2, faces, thickness, tolerance),
1987
+ inputFaceHashes,
1988
+ hashUpperBound,
1989
+ false,
1990
+ shape2
1991
+ );
1992
+ }
1993
+ thickenWithHistory(shape2, thickness, inputFaceHashes, hashUpperBound) {
1994
+ return this.buildEvolution(
1995
+ this.thicken(shape2, thickness),
1996
+ inputFaceHashes,
1997
+ hashUpperBound,
1998
+ false,
1999
+ shape2
2000
+ );
2001
+ }
2002
+ offsetWithHistory(shape2, distance, inputFaceHashes, hashUpperBound, tolerance) {
2003
+ return this.buildEvolution(
2004
+ this.offset(shape2, distance, tolerance),
2005
+ inputFaceHashes,
2006
+ hashUpperBound,
2007
+ false,
2008
+ shape2
2009
+ );
2010
+ }
2011
+ // ═══════════════════════════════════════════════════════════════════════
2012
+ // Meshing
2013
+ // ═══════════════════════════════════════════════════════════════════════
2014
+ mesh(shape2, options) {
2015
+ if (options.angularTolerance > 0) {
2016
+ warnOnce(
2017
+ "mesh-angular",
2018
+ "mesh angularTolerance is not supported; only linear deflection is used."
2019
+ );
2020
+ }
2021
+ const h8 = unwrap(shape2);
2022
+ const bkHandle = shape2;
2023
+ const deflection = options.tolerance || DEFAULT_DEFLECTION;
2024
+ let result;
2025
+ if (bkHandle.type === "solid") {
2026
+ result = this.meshSolid(h8, deflection);
2027
+ } else if (bkHandle.type === "face") {
2028
+ result = this.meshSingleFace(h8, deflection, 0);
2029
+ } else {
2030
+ throw new Error(`brepkit: cannot mesh shape of type '${bkHandle.type}'`);
2031
+ }
2032
+ if (options.skipNormals) {
2033
+ result.normals = new Float32Array(0);
2034
+ }
2035
+ if (!options.includeUVs) {
2036
+ result.uvs = new Float32Array(0);
2037
+ }
2038
+ return result;
2039
+ }
2040
+ meshEdges(shape2, tolerance, angularTolerance) {
2041
+ if (angularTolerance > 0) {
2042
+ warnOnce(
2043
+ "mesh-edges-angular",
2044
+ "meshEdges angularTolerance is not supported; only linear deflection is used."
2045
+ );
2046
+ }
2047
+ const bkHandle = shape2;
2048
+ if (bkHandle.type !== "solid") {
2049
+ return { lines: new Float32Array(0), edgeGroups: [] };
2050
+ }
2051
+ const edgeLines = this.bk.meshEdges(bkHandle.id, tolerance);
2052
+ const positions = edgeLines.positions;
2053
+ const offsets = edgeLines.offsets;
2054
+ const edgeCount = edgeLines.edgeCount;
2055
+ const edgeGroups = [];
2056
+ for (let i7 = 0; i7 < edgeCount; i7++) {
2057
+ const startIdx = offsets[i7];
2058
+ const endIdx = i7 + 1 < edgeCount ? offsets[i7 + 1] : positions.length;
2059
+ const pointCount = (endIdx - startIdx) / 3;
2060
+ edgeGroups.push({ start: startIdx / 3, count: pointCount, edgeHash: i7 });
2061
+ }
2062
+ return {
2063
+ lines: new Float32Array(positions),
2064
+ edgeGroups
2065
+ };
2066
+ }
2067
+ // ═══════════════════════════════════════════════════════════════════════
2068
+ // File I/O
2069
+ // ═══════════════════════════════════════════════════════════════════════
2070
+ exportSTEP(shapes) {
2071
+ if (shapes.length === 0) return "";
2072
+ const parts = [];
2073
+ for (const shape2 of shapes) {
2074
+ const solidIds = unwrapSolidsForExport(this.bk, shape2, "exportSTEP");
2075
+ for (const sid of solidIds) {
2076
+ const bytes = this.bk.exportStep(sid);
2077
+ parts.push(new TextDecoder().decode(bytes));
2078
+ }
2079
+ }
2080
+ return parts.join("\n");
2081
+ }
2082
+ exportSTL(shape2, binary) {
2083
+ const solidIds = unwrapSolidsForExport(this.bk, shape2, "exportSTL");
2084
+ if (binary) {
2085
+ const bytes2 = this.bk.exportStl(solidIds[0], DEFAULT_DEFLECTION);
2086
+ return bytes2.buffer;
2087
+ }
2088
+ const bytes = this.bk.exportStlAscii(solidIds[0], DEFAULT_DEFLECTION);
2089
+ return new TextDecoder().decode(bytes);
2090
+ }
2091
+ importSTEP(data) {
2092
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
2093
+ return toArray(this.bk.importStep(bytes)).map(solidHandle);
2094
+ }
2095
+ importSTL(data) {
2096
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
2097
+ const id = this.bk.importStl(bytes);
2098
+ return solidHandle(id);
2099
+ }
2100
+ exportIGES(shapes) {
2101
+ if (shapes.length === 0) return "";
2102
+ const parts = [];
2103
+ for (const shape2 of shapes) {
2104
+ const solidIds = unwrapSolidsForExport(this.bk, shape2, "exportIGES");
2105
+ for (const sid of solidIds) {
2106
+ const bytes = this.bk.exportIges(sid);
2107
+ parts.push(new TextDecoder().decode(bytes));
2108
+ }
2109
+ }
2110
+ return parts.join("\n");
2111
+ }
2112
+ importIGES(data) {
2113
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
2114
+ return toArray(this.bk.importIges(bytes)).map(solidHandle);
2115
+ }
2116
+ exportSTEPAssembly(parts, _options) {
2117
+ if (parts.length === 0) return "";
2118
+ const shapes = parts.map((p7) => p7.shape);
2119
+ return this.exportSTEP(shapes);
2120
+ }
2121
+ // ═══════════════════════════════════════════════════════════════════════
2122
+ // Measurement
2123
+ // ═══════════════════════════════════════════════════════════════════════
2124
+ volume(shape2) {
2125
+ const h8 = shape2;
2126
+ if (h8.type !== "solid") return 0;
2127
+ return this.bk.volume(unwrap(shape2), DEFAULT_DEFLECTION);
2128
+ }
2129
+ area(shape2) {
2130
+ const h8 = shape2;
2131
+ if (h8.type === "face") {
2132
+ return this.bk.faceArea(unwrap(shape2), DEFAULT_DEFLECTION);
2133
+ }
2134
+ if (h8.type === "solid") {
2135
+ return this.bk.surfaceArea(unwrap(shape2), DEFAULT_DEFLECTION);
2136
+ }
2137
+ if (h8.type === "compound") {
2138
+ const faces = this.iterShapes(shape2, "face");
2139
+ let total = 0;
2140
+ for (const face of faces) {
2141
+ total += this.bk.faceArea(unwrap(face), DEFAULT_DEFLECTION);
2142
+ }
2143
+ return total;
2144
+ }
2145
+ return 0;
2146
+ }
2147
+ length(shape2) {
2148
+ const h8 = shape2;
2149
+ if (h8.type === "edge") {
2150
+ return this.bk.edgeLength(unwrap(shape2));
2151
+ }
2152
+ if (h8.type === "face") {
2153
+ return this.bk.facePerimeter(unwrap(shape2));
2154
+ }
2155
+ if (h8.type === "wire") {
2156
+ return this.bk.wireLength(h8.id);
2157
+ }
2158
+ throw new Error("brepkit: length() requires an edge, wire, or face");
2159
+ }
2160
+ centerOfMass(shape2) {
2161
+ const h8 = shape2;
2162
+ if (h8.type === "solid") {
2163
+ const result = this.bk.centerOfMass(unwrap(shape2), DEFAULT_DEFLECTION);
2164
+ return [result[0], result[1], result[2]];
2165
+ }
2166
+ if (h8.type === "face") {
2167
+ const domain = this.uvBounds(shape2);
2168
+ const uMid = (domain.uMin + domain.uMax) / 2;
2169
+ const vMid = (domain.vMin + domain.vMax) / 2;
2170
+ return this.pointOnSurface(shape2, uMid, vMid);
2171
+ }
2172
+ if (h8.type === "edge") {
2173
+ const verts = this.bk.getEdgeVertices(h8.id);
2174
+ return [
2175
+ (verts[0] + verts[3]) / 2,
2176
+ (verts[1] + verts[4]) / 2,
2177
+ (verts[2] + verts[5]) / 2
2178
+ ];
2179
+ }
2180
+ if (h8.type === "vertex") {
2181
+ return this.vertexPosition(shape2);
2182
+ }
2183
+ const vertices = this.iterShapes(shape2, "vertex");
2184
+ if (vertices.length > 0) {
2185
+ let sx = 0, sy = 0, sz = 0;
2186
+ for (const v9 of vertices) {
2187
+ const p7 = this.vertexPosition(v9);
2188
+ sx += p7[0];
2189
+ sy += p7[1];
2190
+ sz += p7[2];
2191
+ }
2192
+ return [sx / vertices.length, sy / vertices.length, sz / vertices.length];
2193
+ }
2194
+ return [0, 0, 0];
2195
+ }
2196
+ linearCenterOfMass(shape2) {
2197
+ const h8 = shape2;
2198
+ if (h8.type === "edge") {
2199
+ const verts = this.bk.getEdgeVertices(h8.id);
2200
+ return [
2201
+ (verts[0] + verts[3]) / 2,
2202
+ (verts[1] + verts[4]) / 2,
2203
+ (verts[2] + verts[5]) / 2
2204
+ ];
2205
+ }
2206
+ return this.centerOfMass(shape2);
2207
+ }
2208
+ boundingBox(shape2) {
2209
+ const h8 = shape2;
2210
+ if (h8.type === "solid") {
2211
+ const bb = this.bk.boundingBox(unwrap(shape2));
2212
+ return {
2213
+ min: [bb[0], bb[1], bb[2]],
2214
+ max: [bb[3], bb[4], bb[5]]
2215
+ };
2216
+ }
2217
+ if (h8.type === "vertex") {
2218
+ const pos = this.vertexPosition(shape2);
2219
+ return { min: [...pos], max: [...pos] };
2220
+ }
2221
+ const vertices = this.iterShapes(shape2, "vertex");
2222
+ if (vertices.length === 0) {
2223
+ return { min: [0, 0, 0], max: [0, 0, 0] };
2224
+ }
2225
+ const first = this.vertexPosition(vertices[0]);
2226
+ let minX = first[0], minY = first[1], minZ = first[2];
2227
+ let maxX = first[0], maxY = first[1], maxZ = first[2];
2228
+ for (let i7 = 1; i7 < vertices.length; i7++) {
2229
+ const p7 = this.vertexPosition(vertices[i7]);
2230
+ if (p7[0] < minX) minX = p7[0];
2231
+ if (p7[0] > maxX) maxX = p7[0];
2232
+ if (p7[1] < minY) minY = p7[1];
2233
+ if (p7[1] > maxY) maxY = p7[1];
2234
+ if (p7[2] < minZ) minZ = p7[2];
2235
+ if (p7[2] > maxZ) maxZ = p7[2];
2236
+ }
2237
+ return { min: [minX, minY, minZ], max: [maxX, maxY, maxZ] };
2238
+ }
2239
+ // ═══════════════════════════════════════════════════════════════════════
2240
+ // Topology introspection
2241
+ // ═══════════════════════════════════════════════════════════════════════
2242
+ iterShapes(shape2, type) {
2243
+ const h8 = unwrap(shape2);
2244
+ const bkHandle = shape2;
2245
+ switch (bkHandle.type) {
2246
+ case "compound": {
2247
+ const children = syntheticCompounds.get(h8);
2248
+ if (children) {
2249
+ const results = [];
2250
+ for (const child of children) {
2251
+ if (child.type === type) {
2252
+ results.push(child);
2253
+ } else {
2254
+ results.push(...this.iterShapes(child, type));
2255
+ }
2256
+ }
2257
+ return results;
2258
+ }
2259
+ if (type === "solid") {
2260
+ return toArray(this.bk.getCompoundSolids(h8)).map(solidHandle);
2261
+ }
2262
+ if (type === "face" || type === "edge" || type === "vertex" || type === "wire") {
2263
+ const solids = toArray(this.bk.getCompoundSolids(h8)).map(solidHandle);
2264
+ return solids.flatMap((s6) => this.iterShapes(s6, type));
2265
+ }
2266
+ return [];
2267
+ }
2268
+ case "solid": {
2269
+ switch (type) {
2270
+ case "face":
2271
+ return toArray(this.bk.getSolidFaces(h8)).map(faceHandle);
2272
+ case "edge":
2273
+ return toArray(this.bk.getSolidEdges(h8)).map(edgeHandle);
2274
+ case "vertex":
2275
+ return toArray(this.bk.getSolidVertices(h8)).map(vertexHandle);
2276
+ case "wire":
2277
+ return toArray(this.bk.getSolidFaces(h8)).flatMap(
2278
+ (faceId) => toArray(this.bk.getFaceWires(faceId)).map(wireHandle)
2279
+ );
2280
+ default:
2281
+ return [];
2282
+ }
2283
+ }
2284
+ case "shell": {
2285
+ if (type === "face") {
2286
+ return toArray(this.bk.getShellFaces(h8)).map(faceHandle);
2287
+ }
2288
+ if (type === "edge" || type === "vertex") {
2289
+ const faces = toArray(this.bk.getShellFaces(h8)).map(faceHandle);
2290
+ const seen = /* @__PURE__ */ new Set();
2291
+ const results = [];
2292
+ for (const face of faces) {
2293
+ for (const child of this.iterShapes(face, type)) {
2294
+ const childId = unwrap(child);
2295
+ if (!seen.has(childId)) {
2296
+ seen.add(childId);
2297
+ results.push(child);
2298
+ }
2299
+ }
2300
+ }
2301
+ return results;
2302
+ }
2303
+ return [];
2304
+ }
2305
+ case "face": {
2306
+ if (type === "face") {
2307
+ return [shape2];
2308
+ }
2309
+ if (type === "edge") {
2310
+ return toArray(this.bk.getFaceEdges(h8)).map(edgeHandle);
2311
+ }
2312
+ if (type === "vertex") {
2313
+ return toArray(this.bk.getFaceVertices(h8)).map(vertexHandle);
2314
+ }
2315
+ if (type === "wire") {
2316
+ return toArray(this.bk.getFaceWires(h8)).map(wireHandle);
2317
+ }
2318
+ return [];
2319
+ }
2320
+ case "wire": {
2321
+ if (type === "wire") {
2322
+ return [shape2];
2323
+ }
2324
+ if (type === "edge") {
2325
+ return toArray(this.bk.getWireEdges(h8)).map(edgeHandle);
2326
+ }
2327
+ if (type === "vertex") {
2328
+ const edgeIds = toArray(this.bk.getWireEdges(h8));
2329
+ const seen = /* @__PURE__ */ new Set();
2330
+ const results = [];
2331
+ for (const eid of edgeIds) {
2332
+ const verts = this.bk.getEdgeVertices(eid);
2333
+ const coords = [
2334
+ [verts[0], verts[1], verts[2]],
2335
+ [verts[3], verts[4], verts[5]]
2336
+ ];
2337
+ for (const [x7, y6, z6] of coords) {
2338
+ const key = `${x7},${y6},${z6}`;
2339
+ if (!seen.has(key)) {
2340
+ seen.add(key);
2341
+ results.push(vertexHandle(this.bk.makeVertex(x7, y6, z6)));
2342
+ }
2343
+ }
2344
+ }
2345
+ return results;
2346
+ }
2347
+ return [];
2348
+ }
2349
+ case "edge": {
2350
+ if (type === "edge") {
2351
+ return [shape2];
2352
+ }
2353
+ if (type === "vertex") {
2354
+ const verts = this.bk.getEdgeVertices(h8);
2355
+ const v1 = this.bk.makeVertex(verts[0], verts[1], verts[2]);
2356
+ const v22 = this.bk.makeVertex(verts[3], verts[4], verts[5]);
2357
+ return [vertexHandle(v1), vertexHandle(v22)];
2358
+ }
2359
+ return [];
2360
+ }
2361
+ default:
2362
+ return [];
2363
+ }
2364
+ }
2365
+ iterShapeList(list, callback) {
2366
+ if (Array.isArray(list)) {
2367
+ for (const item of list) callback(item);
2368
+ }
2369
+ }
2370
+ shapeType(shape2) {
2371
+ if (isBrepkitHandle(shape2)) return shape2.type;
2372
+ throw new Error("brepkit: cannot determine shape type of non-brepkit handle");
2373
+ }
2374
+ isSame(a9, b10) {
2375
+ return isBrepkitHandle(a9) && isBrepkitHandle(b10) && a9.id === b10.id && a9.type === b10.type;
2376
+ }
2377
+ isEqual(a9, b10) {
2378
+ return this.isSame(a9, b10);
2379
+ }
2380
+ downcast(shape2, _type) {
2381
+ return shape2;
2382
+ }
2383
+ hashCode(shape2, upperBound) {
2384
+ if (!isBrepkitHandle(shape2)) return 0;
2385
+ return shape2.id % upperBound;
2386
+ }
2387
+ isNull(shape2) {
2388
+ return !shape2 || !isBrepkitHandle(shape2);
2389
+ }
2390
+ shapeOrientation(shape2) {
2391
+ const h8 = unwrap(shape2);
2392
+ const orient = this.bk.getShapeOrientation(h8);
2393
+ return orient;
2394
+ }
2395
+ // ═══════════════════════════════════════════════════════════════════════
2396
+ // Geometry queries: vertex
2397
+ // ═══════════════════════════════════════════════════════════════════════
2398
+ vertexPosition(vertex) {
2399
+ const pos = this.bk.getVertexPosition(unwrap(vertex, "vertex"));
2400
+ return [pos[0], pos[1], pos[2]];
2401
+ }
2402
+ // ═══════════════════════════════════════════════════════════════════════
2403
+ // Geometry queries: face / surface
2404
+ // ═══════════════════════════════════════════════════════════════════════
2405
+ surfaceType(face) {
2406
+ const typeStr = this.bk.getSurfaceType(unwrap(face, "face"));
2407
+ return typeStr;
2408
+ }
2409
+ uvBounds(face) {
2410
+ const domain = this.bk.getSurfaceDomain(unwrap(face, "face"));
2411
+ return { uMin: domain[0], uMax: domain[1], vMin: domain[2], vMax: domain[3] };
2412
+ }
2413
+ outerWire(face) {
2414
+ const id = this.bk.getFaceOuterWire(unwrap(face, "face"));
2415
+ return wireHandle(id);
2416
+ }
2417
+ surfaceNormal(face, u7, v9) {
2418
+ const n9 = this.bk.evaluateSurfaceNormal(unwrap(face, "face"), u7, v9);
2419
+ return [n9[0], n9[1], n9[2]];
2420
+ }
2421
+ pointOnSurface(face, u7, v9) {
2422
+ const p7 = this.bk.evaluateSurface(unwrap(face, "face"), u7, v9);
2423
+ return [p7[0], p7[1], p7[2]];
2424
+ }
2425
+ uvFromPoint(face, point) {
2426
+ try {
2427
+ const result = this.bk.projectPointOnSurface(
2428
+ unwrap(face, "face"),
2429
+ point[0],
2430
+ point[1],
2431
+ point[2]
2432
+ );
2433
+ return [result[0], result[1]];
2434
+ } catch (e8) {
2435
+ console.warn("brepkit: uvFromPoint failed:", e8);
2436
+ return null;
2437
+ }
2438
+ }
2439
+ projectPointOnFace(face, point) {
2440
+ const result = this.bk.projectPointOnSurface(
2441
+ unwrap(face, "face"),
2442
+ point[0],
2443
+ point[1],
2444
+ point[2]
2445
+ );
2446
+ return [result[2], result[3], result[4]];
2447
+ }
2448
+ // ═══════════════════════════════════════════════════════════════════════
2449
+ // Geometry queries: edge / curve
2450
+ // ═══════════════════════════════════════════════════════════════════════
2451
+ curveTangent(shape2, param) {
2452
+ const h8 = shape2;
2453
+ let edgeId;
2454
+ let evalParam = param;
2455
+ if (h8.type === "wire") {
2456
+ const edgeIds = toArray(this.bk.getWireEdges(h8.id));
2457
+ edgeId = edgeIds[edgeIds.length - 1];
2458
+ let cumulative = 0;
2459
+ for (const eid of edgeIds) {
2460
+ const p7 = this.bk.getEdgeCurveParameters(eid);
2461
+ const span = p7[1] - p7[0];
2462
+ if (param <= cumulative + span || eid === edgeId) {
2463
+ edgeId = eid;
2464
+ evalParam = Math.min(p7[0] + (param - cumulative), p7[1]);
2465
+ break;
2466
+ }
2467
+ cumulative += span;
2468
+ }
2469
+ } else {
2470
+ edgeId = unwrap(shape2, "edge");
2471
+ }
2472
+ const result = this.bk.evaluateEdgeCurveD1(edgeId, evalParam);
2473
+ return {
2474
+ point: [result[0], result[1], result[2]],
2475
+ tangent: [result[3], result[4], result[5]]
2476
+ };
2477
+ }
2478
+ curveParameters(shape2) {
2479
+ const h8 = shape2;
2480
+ if (h8.type === "wire") {
2481
+ const edgeIds = toArray(this.bk.getWireEdges(h8.id));
2482
+ if (edgeIds.length === 0) return [0, 0];
2483
+ let total = 0;
2484
+ for (const eid of edgeIds) {
2485
+ const p7 = this.bk.getEdgeCurveParameters(eid);
2486
+ total += p7[1] - p7[0];
2487
+ }
2488
+ return [0, total];
2489
+ }
2490
+ const edgeId = unwrap(shape2, "edge");
2491
+ const params = this.bk.getEdgeCurveParameters(edgeId);
2492
+ return [params[0], params[1]];
2493
+ }
2494
+ curvePointAtParam(shape2, param) {
2495
+ const h8 = shape2;
2496
+ if (h8.type === "wire") {
2497
+ const edgeIds = toArray(this.bk.getWireEdges(h8.id));
2498
+ let cumulative = 0;
2499
+ for (const eid of edgeIds) {
2500
+ const p22 = this.bk.getEdgeCurveParameters(eid);
2501
+ const span = p22[1] - p22[0];
2502
+ if (param <= cumulative + span || eid === edgeIds[edgeIds.length - 1]) {
2503
+ const localParam = p22[0] + (param - cumulative);
2504
+ const pt2 = this.bk.evaluateEdgeCurve(eid, Math.min(localParam, p22[1]));
2505
+ return [pt2[0], pt2[1], pt2[2]];
2506
+ }
2507
+ cumulative += span;
2508
+ }
2509
+ const pt = this.bk.evaluateEdgeCurve(edgeIds[0], param);
2510
+ return [pt[0], pt[1], pt[2]];
2511
+ }
2512
+ const edgeId = unwrap(shape2, "edge");
2513
+ const p7 = this.bk.evaluateEdgeCurve(edgeId, param);
2514
+ return [p7[0], p7[1], p7[2]];
2515
+ }
2516
+ curveIsClosed(shape2) {
2517
+ const h8 = shape2;
2518
+ if (h8.type === "wire") {
2519
+ const edgeIds = toArray(this.bk.getWireEdges(h8.id));
2520
+ if (edgeIds.length === 0) return false;
2521
+ if (edgeIds.length === 1) {
2522
+ const verts2 = this.bk.getEdgeVertices(edgeIds[0]);
2523
+ return dist3(verts2[0], verts2[1], verts2[2], verts2[3], verts2[4], verts2[5]) < 1e-7;
2524
+ }
2525
+ const endpoints = [];
2526
+ for (const eid of edgeIds) {
2527
+ const verts2 = this.bk.getEdgeVertices(eid);
2528
+ endpoints.push([verts2[0], verts2[1], verts2[2]]);
2529
+ endpoints.push([verts2[3], verts2[4], verts2[5]]);
2530
+ }
2531
+ const unmatched = [];
2532
+ for (const pt of endpoints) {
2533
+ const matchIdx = unmatched.findIndex(
2534
+ (u7) => dist3(u7[0], u7[1], u7[2], pt[0], pt[1], pt[2]) < 1e-7
2535
+ );
2536
+ if (matchIdx >= 0) {
2537
+ unmatched.splice(matchIdx, 1);
2538
+ } else {
2539
+ unmatched.push(pt);
2540
+ }
2541
+ }
2542
+ return unmatched.length === 0;
2543
+ }
2544
+ const verts = this.bk.getEdgeVertices(unwrap(shape2, "edge"));
2545
+ return dist3(verts[0], verts[1], verts[2], verts[3], verts[4], verts[5]) < 1e-7;
2546
+ }
2547
+ curveIsPeriodic(shape2) {
2548
+ const h8 = shape2;
2549
+ try {
2550
+ if (h8.type === "edge") return this.curveIsClosed(shape2);
2551
+ if (h8.type === "wire") {
2552
+ const edgeIds = toArray(this.bk.getWireEdges(h8.id));
2553
+ if (edgeIds.length === 1) return this.curveIsClosed(shape2);
2554
+ }
2555
+ } catch {
2556
+ }
2557
+ return false;
2558
+ }
2559
+ curvePeriod(shape2) {
2560
+ try {
2561
+ if (this.curveIsPeriodic(shape2)) {
2562
+ const bounds = this.curveParameters(shape2);
2563
+ return bounds[1] - bounds[0];
2564
+ }
2565
+ } catch {
2566
+ }
2567
+ return 0;
2568
+ }
2569
+ curveType(shape2) {
2570
+ const h8 = shape2;
2571
+ if (h8.type === "wire") {
2572
+ const edges = this.iterShapes(shape2, "edge");
2573
+ const first = edges[0];
2574
+ if (first) return this.bk.getEdgeCurveType(unwrap(first, "edge"));
2575
+ return "LINE";
2576
+ }
2577
+ return this.bk.getEdgeCurveType(unwrap(shape2, "edge"));
2578
+ }
2579
+ // ═══════════════════════════════════════════════════════════════════════
2580
+ // Simplification & repair
2581
+ // ═══════════════════════════════════════════════════════════════════════
2582
+ simplify(shape2) {
2583
+ if (shape2.type === "solid") {
2584
+ try {
2585
+ this.bk.healSolid(unwrap(shape2));
2586
+ } catch (e8) {
2587
+ console.warn("brepkit: healing failed in simplify:", e8);
2588
+ }
2589
+ }
2590
+ return shape2;
2591
+ }
2592
+ isValid(shape2) {
2593
+ if (!isBrepkitHandle(shape2)) return false;
2594
+ if (shape2.type !== "solid") return true;
2595
+ try {
2596
+ const errors = this.bk.validateSolidRelaxed(shape2.id);
2597
+ return errors === 0;
2598
+ } catch (e8) {
2599
+ console.warn("brepkit: isValid check failed:", e8);
2600
+ return false;
2601
+ }
2602
+ }
2603
+ isValidStrict(shape2) {
2604
+ if (!isBrepkitHandle(shape2)) return false;
2605
+ if (shape2.type !== "solid") return true;
2606
+ try {
2607
+ const errors = this.bk.validateSolid(shape2.id);
2608
+ return errors === 0;
2609
+ } catch (e8) {
2610
+ console.warn("brepkit: isValidStrict check failed:", e8);
2611
+ return false;
2612
+ }
2613
+ }
2614
+ sew(shapes, tolerance) {
2615
+ const faceIds = [];
2616
+ for (const s6 of shapes) {
2617
+ const h8 = s6;
2618
+ if (h8.type === "face") {
2619
+ faceIds.push(h8.id);
2620
+ } else if (h8.type === "solid") {
2621
+ for (const fid of toArray(this.bk.getSolidFaces(h8.id))) {
2622
+ faceIds.push(fid);
2623
+ }
2624
+ } else if (h8.type === "shell") {
2625
+ for (const fid of toArray(this.bk.getShellFaces(h8.id))) {
2626
+ faceIds.push(fid);
2627
+ }
2628
+ }
2629
+ }
2630
+ const tol = tolerance ?? 1e-7;
2631
+ try {
2632
+ const id2 = this.bk.weldShellsAndFaces(faceIds, tol);
2633
+ return shellHandle(id2);
2634
+ } catch (e8) {
2635
+ console.warn("brepkit: weldShellsAndFaces failed, falling back to sewFaces:", e8);
2636
+ }
2637
+ const id = this.bk.sewFaces(faceIds, tol);
2638
+ return shellHandle(id);
2639
+ }
2640
+ healSolid(shape2) {
2641
+ const h8 = shape2;
2642
+ if (h8.type !== "solid") {
2643
+ throw new Error(
2644
+ `brepkit: healSolid requires a solid, got ${h8.type}. Consider using makeCompound() to combine shapes first.`
2645
+ );
2646
+ }
2647
+ try {
2648
+ const remaining = this.bk.repairSolid(unwrap(shape2));
2649
+ if (remaining > 0) {
2650
+ console.warn(`brepkit: repairSolid left ${remaining} error(s) on solid.`);
2651
+ }
2652
+ return shape2;
2653
+ } catch (e8) {
2654
+ try {
2655
+ this.bk.healSolid(unwrap(shape2));
2656
+ return shape2;
2657
+ } catch (healErr) {
2658
+ console.warn(
2659
+ "brepkit: healSolid failed (repairSolid error:",
2660
+ e8,
2661
+ ", healSolid error:",
2662
+ healErr,
2663
+ ")"
2664
+ );
2665
+ return null;
2666
+ }
2667
+ }
2668
+ }
2669
+ healFace(shape2) {
2670
+ return shape2;
2671
+ }
2672
+ healWire(wire, _face) {
2673
+ return wire;
2674
+ }
2675
+ // ═══════════════════════════════════════════════════════════════════════
2676
+ // 2D offset
2677
+ // ═══════════════════════════════════════════════════════════════════════
2678
+ offsetWire2D(wire, offset2, _joinType) {
2679
+ const edges = this.iterShapes(wire, "edge");
2680
+ if (edges.length === 0) return wire;
2681
+ const coords2d = [];
2682
+ for (const edge of edges) {
2683
+ const verts = this.bk.getEdgeVertices(unwrap(edge, "edge"));
2684
+ coords2d.push(verts[0], verts[1]);
2685
+ }
2686
+ if (coords2d.length < 6) return wire;
2687
+ const result = this.bk.offsetPolygon2d(coords2d, offset2, 1e-10);
2688
+ const coords3d = [];
2689
+ for (let i7 = 0; i7 < result.length; i7 += 2) {
2690
+ coords3d.push(result[i7], result[i7 + 1], 0);
2691
+ }
2692
+ const wireId = this.bk.makePolygonWire(coords3d);
2693
+ return wireHandle(wireId);
2694
+ }
2695
+ // ═══════════════════════════════════════════════════════════════════════
2696
+ // Distance
2697
+ // ═══════════════════════════════════════════════════════════════════════
2698
+ distance(shape1, shape2) {
2699
+ const h1 = shape1;
2700
+ const h22 = shape2;
2701
+ if (h1.type === "solid" && h22.type === "solid") {
2702
+ const d10 = this.bk.solidToSolidDistance(h1.id, h22.id);
2703
+ return { value: d10, point1: [0, 0, 0], point2: [0, 0, 0] };
2704
+ }
2705
+ if (h1.type === "vertex" && h22.type === "solid") {
2706
+ const pos = this.bk.getVertexPosition(h1.id);
2707
+ const result = this.bk.pointToSolidDistance(pos[0], pos[1], pos[2], h22.id);
2708
+ return {
2709
+ value: result[0],
2710
+ point1: [pos[0], pos[1], pos[2]],
2711
+ point2: [result[1], result[2], result[3]]
2712
+ };
2713
+ }
2714
+ if (h1.type === "vertex" && h22.type === "face") {
2715
+ const pos = this.bk.getVertexPosition(h1.id);
2716
+ const result = this.bk.pointToFaceDistance(pos[0], pos[1], pos[2], h22.id);
2717
+ return {
2718
+ value: result[0],
2719
+ point1: [pos[0], pos[1], pos[2]],
2720
+ point2: [result[1], result[2], result[3]]
2721
+ };
2722
+ }
2723
+ if (h1.type === "vertex" && h22.type === "edge") {
2724
+ const pos = this.bk.getVertexPosition(h1.id);
2725
+ const result = this.bk.pointToEdgeDistance(pos[0], pos[1], pos[2], h22.id);
2726
+ return {
2727
+ value: result[0],
2728
+ point1: [pos[0], pos[1], pos[2]],
2729
+ point2: [result[1], result[2], result[3]]
2730
+ };
2731
+ }
2732
+ const getPos = (s6) => {
2733
+ if (s6.type === "vertex") {
2734
+ const p7 = this.bk.getVertexPosition(s6.id);
2735
+ return [p7[0], p7[1], p7[2]];
2736
+ }
2737
+ if (s6.type === "solid") {
2738
+ const bb = this.bk.boundingBox(s6.id);
2739
+ return [(bb[0] + bb[3]) / 2, (bb[1] + bb[4]) / 2, (bb[2] + bb[5]) / 2];
2740
+ }
2741
+ return [0, 0, 0];
2742
+ };
2743
+ const p1 = getPos(h1);
2744
+ const p22 = getPos(h22);
2745
+ const dx = p22[0] - p1[0];
2746
+ const dy = p22[1] - p1[1];
2747
+ const dz = p22[2] - p1[2];
2748
+ return { value: Math.sqrt(dx * dx + dy * dy + dz * dz), point1: p1, point2: p22 };
2749
+ }
2750
+ // ═══════════════════════════════════════════════════════════════════════
2751
+ // Classification
2752
+ // ═══════════════════════════════════════════════════════════════════════
2753
+ classifyPointOnFace(face, u7, v9, tolerance) {
2754
+ if (tolerance !== void 0) {
2755
+ warnOnce(
2756
+ "classify-tolerance",
2757
+ "classifyPointOnFace() tolerance parameter is not supported; brepkit uses domain-based classification."
2758
+ );
2759
+ }
2760
+ const faceId = unwrap(face, "face");
2761
+ const domain = this.bk.getSurfaceDomain(faceId);
2762
+ if (u7 < domain[0] || u7 > domain[1] || v9 < domain[2] || v9 > domain[3]) {
2763
+ return "out";
2764
+ }
2765
+ return "in";
2766
+ }
2767
+ // ═══════════════════════════════════════════════════════════════════════
2768
+ // Curve construction
2769
+ // ═══════════════════════════════════════════════════════════════════════
2770
+ interpolatePoints(points, options) {
2771
+ if (options?.tolerance !== void 0) {
2772
+ warnOnce(
2773
+ "interpolate-tolerance",
2774
+ "interpolatePoints() tolerance parameter is not supported; brepkit uses chord-length parameterisation."
2775
+ );
2776
+ }
2777
+ if (points.length < 2) throw new Error("brepkit: need at least 2 points");
2778
+ if (points.length === 2) {
2779
+ return this.makeLineEdge(points[0], points[1]);
2780
+ }
2781
+ const degree = Math.min(3, points.length - 1);
2782
+ const coords = points.flatMap(([x7, y6, z6]) => [x7, y6, z6]);
2783
+ const id = this.bk.interpolatePoints(coords, degree);
2784
+ return edgeHandle(id);
2785
+ }
2786
+ approximatePoints(points, options) {
2787
+ const degree = options?.degMax ?? 3;
2788
+ const tol = options?.tolerance ?? 1e-6;
2789
+ const coords = [];
2790
+ for (const p7 of points) coords.push(p7[0], p7[1], p7[2]);
2791
+ const numCps = Math.max(degree + 1, Math.min(points.length, Math.ceil(points.length * 0.7)));
2792
+ const id = this.bk.approximateCurveLspia(coords, degree, numCps, tol, 100);
2793
+ return edgeHandle(id);
2794
+ }
2795
+ // ═══════════════════════════════════════════════════════════════════════
2796
+ // Serialization
2797
+ // ═══════════════════════════════════════════════════════════════════════
2798
+ toBREP(shape2) {
2799
+ warnOnce(
2800
+ "brep-format",
2801
+ "toBREP/fromBREP uses STEP format (not OCCT BREP). Cross-kernel BREP round-trips are not supported."
2802
+ );
2803
+ return this.exportSTEP([shape2]);
2804
+ }
2805
+ fromBREP(data) {
2806
+ warnOnce(
2807
+ "brep-format",
2808
+ "toBREP/fromBREP uses STEP format (not OCCT BREP). Cross-kernel BREP round-trips are not supported."
2809
+ );
2810
+ const shapes = this.importSTEP(data);
2811
+ if (shapes.length === 0) throw new Error("brepkit: fromBREP produced no shapes");
2812
+ return shapes[0];
2813
+ }
2814
+ // ═══════════════════════════════════════════════════════════════════════
2815
+ // Mesh preparation
2816
+ // ═══════════════════════════════════════════════════════════════════════
2817
+ hasTriangulation(_shape) {
2818
+ return false;
2819
+ }
2820
+ meshShape(_shape, _tolerance, _angularTolerance) {
2821
+ }
2822
+ // ═══════════════════════════════════════════════════════════════════════
2823
+ // Composed transforms
2824
+ // ═══════════════════════════════════════════════════════════════════════
2825
+ composeTransform(ops) {
2826
+ let matrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
2827
+ for (const op of ops) {
2828
+ const m10 = op.type === "translate" ? translationMatrix(op.x, op.y, op.z) : rotationMatrix(op.angle, op.axis, op.center);
2829
+ matrix = multiplyMatrices(m10, matrix);
2830
+ }
2831
+ return { handle: matrix, dispose: () => {
2832
+ } };
2833
+ }
2834
+ applyComposedTransformWithHistory(shape2, transformHandle, inputFaceHashes, hashUpperBound) {
2835
+ const result = this.applyMatrix(shape2, transformHandle);
2836
+ return this.buildEvolution(result, inputFaceHashes, hashUpperBound, true);
2837
+ }
2838
+ // ═══════════════════════════════════════════════════════════════════════
2839
+ // Advanced sweep/loft
2840
+ // ═══════════════════════════════════════════════════════════════════════
2841
+ sweepPipeShell(profile, spine, options) {
2842
+ const profileHandle = profile;
2843
+ const faceId = profileHandle.type === "wire" ? this.bk.makeFaceFromWire(profileHandle.id) : unwrap(profile, "face");
2844
+ const shellMode = !!(options && options["shellMode"]);
2845
+ const nurbsData = this.extractNurbsFromEdge(spine);
2846
+ if (nurbsData && nurbsData.degree > 1) {
2847
+ try {
2848
+ const id = this.bk.sweepSmooth(
2849
+ faceId,
2850
+ nurbsData.degree,
2851
+ nurbsData.knots,
2852
+ nurbsData.controlPoints,
2853
+ nurbsData.weights
2854
+ );
2855
+ const shape22 = solidHandle(id);
2856
+ if (shellMode) return { shape: shape22, firstShape: profile, lastShape: profile };
2857
+ return shape22;
2858
+ } catch (e8) {
2859
+ console.warn("brepkit: sweepSmooth failed, falling back to simplePipe:", e8);
2860
+ }
2861
+ }
2862
+ const shape2 = this.simplePipe(profile, spine);
2863
+ if (shellMode) return { shape: shape2, firstShape: profile, lastShape: profile };
2864
+ return shape2;
2865
+ }
2866
+ loftAdvanced(wires, options) {
2867
+ const faceIds = wires.map((w6) => {
2868
+ const h8 = w6;
2869
+ if (h8.type === "wire") return this.bk.makeFaceFromWire(h8.id);
2870
+ return unwrap(w6, "face");
2871
+ });
2872
+ try {
2873
+ const opts = {};
2874
+ if (options?.ruled !== void 0) opts["ruled"] = options.ruled;
2875
+ if (options?.solid !== void 0) opts["solid"] = options.solid;
2876
+ if (options?.tolerance !== void 0) opts["tolerance"] = options.tolerance;
2877
+ if (options?.startVertex) {
2878
+ const pos = this.bk.getVertexPosition(unwrap(options.startVertex, "vertex"));
2879
+ opts["startPoint"] = [pos[0], pos[1], pos[2]];
2880
+ }
2881
+ if (options?.endVertex) {
2882
+ const pos = this.bk.getVertexPosition(unwrap(options.endVertex, "vertex"));
2883
+ opts["endPoint"] = [pos[0], pos[1], pos[2]];
2884
+ }
2885
+ const id = this.bk.loftWithOptions(faceIds, JSON.stringify(opts));
2886
+ return solidHandle(id);
2887
+ } catch (e8) {
2888
+ console.warn("brepkit: loftWithOptions failed, falling back to smooth/basic loft:", e8);
2889
+ }
2890
+ if (!options?.ruled) {
2891
+ try {
2892
+ const id = this.bk.loftSmooth(faceIds);
2893
+ return solidHandle(id);
2894
+ } catch (e8) {
2895
+ console.warn("brepkit: loftSmooth failed, falling back to basic loft:", e8);
2896
+ }
2897
+ }
2898
+ return this.loft(wires);
2899
+ }
2900
+ buildExtrusionLaw(profile, length, endFactor) {
2901
+ const law = {
2902
+ type: "extrusionLaw",
2903
+ profile,
2904
+ length,
2905
+ endFactor,
2906
+ Trim(_first, _last, _tol) {
2907
+ return law;
2908
+ },
2909
+ delete: noop
2910
+ };
2911
+ return law;
2912
+ }
2913
+ // ═══════════════════════════════════════════════════════════════════════
2914
+ // Curve positioning & patterns
2915
+ // ═══════════════════════════════════════════════════════════════════════
2916
+ positionOnCurve(shape2, spine, param) {
2917
+ const { point, tangent } = this.curveTangent(spine, param);
2918
+ const [tx, ty, tz] = tangent;
2919
+ const len = Math.sqrt(tx * tx + ty * ty + tz * tz);
2920
+ if (len < 1e-12) return this.translate(shape2, point[0], point[1], point[2]);
2921
+ const nx = tx / len, ny = ty / len, nz = tz / len;
2922
+ const dot = nz;
2923
+ let result = shape2;
2924
+ if (Math.abs(dot + 1) < 1e-10) {
2925
+ result = this.rotate(result, 180, [1, 0, 0]);
2926
+ } else if (Math.abs(dot - 1) > 1e-10) {
2927
+ const axis = [-ny, nx, 0];
2928
+ const angleDeg = Math.acos(Math.max(-1, Math.min(1, dot))) * (180 / Math.PI);
2929
+ result = this.rotate(result, angleDeg, axis);
2930
+ }
2931
+ return this.translate(result, point[0], point[1], point[2]);
2932
+ }
2933
+ linearPattern(shape2, direction, spacing, count) {
2934
+ const results = [shape2];
2935
+ for (let i7 = 1; i7 < count; i7++) {
2936
+ const offset2 = spacing * i7;
2937
+ results.push(
2938
+ this.translate(shape2, direction[0] * offset2, direction[1] * offset2, direction[2] * offset2)
2939
+ );
2940
+ }
2941
+ return results;
2942
+ }
2943
+ circularPattern(shape2, center, axis, angleStep, count) {
2944
+ const results = [shape2];
2945
+ for (let i7 = 1; i7 < count; i7++) {
2946
+ results.push(this.rotate(shape2, angleStep * i7, axis, center));
2947
+ }
2948
+ return results;
2949
+ }
2950
+ gridPattern(shape2, directionX, directionY, spacingX, spacingY, countX, countY) {
2951
+ const id = this.bk.gridPattern(
2952
+ unwrapSolidOrThrow(shape2, "gridPattern"),
2953
+ directionX[0],
2954
+ directionX[1],
2955
+ directionX[2],
2956
+ directionY[0],
2957
+ directionY[1],
2958
+ directionY[2],
2959
+ spacingX,
2960
+ spacingY,
2961
+ countX,
2962
+ countY
2963
+ );
2964
+ return compoundHandle(id);
2965
+ }
2966
+ // ═══════════════════════════════════════════════════════════════════════
2967
+ // Surface construction
2968
+ // ═══════════════════════════════════════════════════════════════════════
2969
+ makeNonPlanarFace(wire) {
2970
+ return this.makeFace(wire, true);
2971
+ }
2972
+ addHolesInFace(face, holeWires) {
2973
+ const wireIds = holeWires.map((w6) => unwrap(w6, "wire"));
2974
+ const id = this.bk.addHolesToFace(unwrap(face, "face"), wireIds);
2975
+ return faceHandle(id);
2976
+ }
2977
+ // TODO: Expose bk.removeHolesFromFace() — inverse of addHolesInFace.
2978
+ // Useful for defeaturing workflows. See brepkitWasmTypes.ts.
2979
+ makeFaceOnSurface(_surface, wire) {
2980
+ return this.makeFace(wire, true);
2981
+ }
2982
+ bsplineSurface(points, rows, cols) {
2983
+ const coords = [];
2984
+ for (const [x7, y6, z6] of points) {
2985
+ coords.push(x7, y6, z6);
2986
+ }
2987
+ const degreeU = Math.min(3, rows - 1);
2988
+ const degreeV = Math.min(3, cols - 1);
2989
+ try {
2990
+ const faceId = this.bk.interpolateSurface(coords, rows, cols, degreeU, degreeV);
2991
+ return faceHandle(faceId);
2992
+ } catch {
2993
+ return this.triangulatedSurface(points, rows, cols);
2994
+ }
2995
+ }
2996
+ triangulatedSurface(points, rows, cols) {
2997
+ const faces = [];
2998
+ for (let r9 = 0; r9 < rows - 1; r9++) {
2999
+ for (let c7 = 0; c7 < cols - 1; c7++) {
3000
+ const i00 = r9 * cols + c7;
3001
+ const i10 = (r9 + 1) * cols + c7;
3002
+ const i01 = r9 * cols + (c7 + 1);
3003
+ const i11 = (r9 + 1) * cols + (c7 + 1);
3004
+ const f1 = this.buildTriFace(points[i00], points[i10], points[i01]);
3005
+ if (f1) faces.push(f1);
3006
+ const f22 = this.buildTriFace(points[i10], points[i11], points[i01]);
3007
+ if (f22) faces.push(f22);
3008
+ }
3009
+ }
3010
+ if (faces.length === 0) throw new Error("brepkit: no valid faces in surface grid");
3011
+ return this.sew(faces, 1e-6);
3012
+ }
3013
+ // ═══════════════════════════════════════════════════════════════════════
3014
+ // Mesh sewing -> solid
3015
+ // ═══════════════════════════════════════════════════════════════════════
3016
+ buildTriFace(a9, b10, c7) {
3017
+ const ab = [b10[0] - a9[0], b10[1] - a9[1], b10[2] - a9[2]];
3018
+ const ac = [c7[0] - a9[0], c7[1] - a9[1], c7[2] - a9[2]];
3019
+ const cross = [
3020
+ ab[1] * ac[2] - ab[2] * ac[1],
3021
+ ab[2] * ac[0] - ab[0] * ac[2],
3022
+ ab[0] * ac[1] - ab[1] * ac[0]
3023
+ ];
3024
+ const area = Math.sqrt(cross[0] ** 2 + cross[1] ** 2 + cross[2] ** 2);
3025
+ if (area < 1e-12) return null;
3026
+ try {
3027
+ const e1 = this.makeLineEdge(a9, b10);
3028
+ const e22 = this.makeLineEdge(b10, c7);
3029
+ const e32 = this.makeLineEdge(c7, a9);
3030
+ const wire = this.makeWire([e1, e22, e32]);
3031
+ return this.makeFace(wire);
3032
+ } catch (e8) {
3033
+ console.warn("brepkit: makeNonPlanarFace failed:", e8);
3034
+ return null;
3035
+ }
3036
+ }
3037
+ sewAndSolidify(faces, tolerance) {
3038
+ const faceIds = faces.map((s6) => unwrap(s6, "face"));
3039
+ const solidId = this.bk.sewFaces(faceIds, tolerance);
3040
+ return solidHandle(solidId);
3041
+ }
3042
+ // ═══════════════════════════════════════════════════════════════════════
3043
+ // Repair
3044
+ // ═══════════════════════════════════════════════════════════════════════
3045
+ fixShape(shape2) {
3046
+ const h8 = shape2;
3047
+ if (h8.type === "solid") {
3048
+ this.bk.healSolid(h8.id);
3049
+ }
3050
+ return shape2;
3051
+ }
3052
+ fixSelfIntersection(wire) {
3053
+ return wire;
3054
+ }
3055
+ // ═══════════════════════════════════════════════════════════════════════
3056
+ // Measurement (advanced)
3057
+ // ═══════════════════════════════════════════════════════════════════════
3058
+ surfaceCurvature(face, u7, v9) {
3059
+ const fid = unwrap(face, "face");
3060
+ const data = this.bk.measureCurvatureAtSurface(fid, u7, v9);
3061
+ if (data.length < 8) {
3062
+ throw new Error(
3063
+ `brepkit: measureCurvatureAtSurface returned ${data.length} values, expected 8`
3064
+ );
3065
+ }
3066
+ const k1 = data[0];
3067
+ const k22 = data[1];
3068
+ const gaussian = k1 * k22;
3069
+ const mean = (k1 + k22) / 2;
3070
+ return {
3071
+ gaussian,
3072
+ mean,
3073
+ max: Math.max(k1, k22),
3074
+ min: Math.min(k1, k22),
3075
+ maxDirection: [data[2], data[3], data[4]],
3076
+ minDirection: [data[5], data[6], data[7]]
3077
+ };
3078
+ }
3079
+ surfaceCenterOfMass(face) {
3080
+ const mesh2 = this.bk.tessellateFace(unwrap(face, "face"), 0.1);
3081
+ const pos = mesh2.positions;
3082
+ const idx = mesh2.indices;
3083
+ let cx = 0, cy = 0, cz = 0, totalArea = 0;
3084
+ for (let t11 = 0; t11 < idx.length; t11 += 3) {
3085
+ const i0 = idx[t11] * 3, i1 = idx[t11 + 1] * 3, i22 = idx[t11 + 2] * 3;
3086
+ const tcx = (pos[i0] + pos[i1] + pos[i22]) / 3;
3087
+ const tcy = (pos[i0 + 1] + pos[i1 + 1] + pos[i22 + 1]) / 3;
3088
+ const tcz = (pos[i0 + 2] + pos[i1 + 2] + pos[i22 + 2]) / 3;
3089
+ const ux = pos[i1] - pos[i0], uy = pos[i1 + 1] - pos[i0 + 1], uz = pos[i1 + 2] - pos[i0 + 2];
3090
+ const vx = pos[i22] - pos[i0], vy = pos[i22 + 1] - pos[i0 + 1], vz = pos[i22 + 2] - pos[i0 + 2];
3091
+ const area = 0.5 * Math.sqrt((uy * vz - uz * vy) ** 2 + (uz * vx - ux * vz) ** 2 + (ux * vy - uy * vx) ** 2);
3092
+ cx += tcx * area;
3093
+ cy += tcy * area;
3094
+ cz += tcz * area;
3095
+ totalArea += area;
3096
+ }
3097
+ if (totalArea < 1e-30) return [0, 0, 0];
3098
+ return [cx / totalArea, cy / totalArea, cz / totalArea];
3099
+ }
3100
+ createDistanceQuery(referenceShape) {
3101
+ const distanceFn = (shape2) => this.distance(referenceShape, shape2);
3102
+ return {
3103
+ distanceTo(shape2) {
3104
+ return distanceFn(shape2);
3105
+ },
3106
+ dispose() {
3107
+ }
3108
+ };
3109
+ }
3110
+ // ═══════════════════════════════════════════════════════════════════════
3111
+ // Projection
3112
+ // ═══════════════════════════════════════════════════════════════════════
3113
+ projectEdges(shape2, _cameraOrigin, _cameraDirection, _cameraXAxis) {
3114
+ const edges = this.iterShapes(shape2, "edge");
3115
+ const emptyCompound = edges.length > 0 ? edges[0] : shape2;
3116
+ return {
3117
+ visible: { outline: emptyCompound, smooth: emptyCompound, sharp: emptyCompound },
3118
+ hidden: { outline: emptyCompound, smooth: emptyCompound, sharp: emptyCompound }
3119
+ };
3120
+ }
3121
+ // ═══════════════════════════════════════════════════════════════════════
3122
+ // Draft
3123
+ // ═══════════════════════════════════════════════════════════════════════
3124
+ draftPrism(shape2, face, _baseFace, height, _angleDeg, fuse2) {
3125
+ if (height !== null) {
3126
+ const normal = this.surfaceNormal(face, 0, 0);
3127
+ const extruded = this.extrude(face, normal, height);
3128
+ if (fuse2) {
3129
+ return this.fuse(shape2, extruded);
3130
+ }
3131
+ return extruded;
3132
+ }
3133
+ return shape2;
3134
+ }
3135
+ // ═══════════════════════════════════════════════════════════════════════
3136
+ // XCAF / configured export
3137
+ // ═══════════════════════════════════════════════════════════════════════
3138
+ createXCAFDocument(shapes) {
3139
+ return { __brepkit_xcaf: true, shapes, delete: noop };
3140
+ }
3141
+ writeXCAFToSTEP(doc, _options) {
3142
+ if (doc && doc.__brepkit_xcaf && Array.isArray(doc.shapes)) {
3143
+ return this.exportSTEP(doc.shapes.map((s6) => s6.shape));
3144
+ }
3145
+ return "";
3146
+ }
3147
+ exportSTEPConfigured(shapes, _options) {
3148
+ return this.exportSTEP(shapes.map((s6) => s6.shape));
3149
+ }
3150
+ // ═══════════════════════════════════════════════════════════════════════
3151
+ // Export helpers
3152
+ // ═══════════════════════════════════════════════════════════════════════
3153
+ wrapString(str) {
3154
+ return str;
3155
+ }
3156
+ wrapColor(red, green, blue, alpha) {
3157
+ return [red, green, blue, alpha];
3158
+ }
3159
+ configureStepUnits(_unit, _modelUnit) {
3160
+ }
3161
+ configureStepWriter(_writer) {
3162
+ }
3163
+ // ═══════════════════════════════════════════════════════════════════════
3164
+ // Curve adaptor
3165
+ // ═══════════════════════════════════════════════════════════════════════
3166
+ createCurveAdaptor(shape2) {
3167
+ return shape2;
3168
+ }
3169
+ // ═══════════════════════════════════════════════════════════════════════
3170
+ // Bezier pole extraction
3171
+ // ═══════════════════════════════════════════════════════════════════════
3172
+ getBezierPenultimatePole(edge) {
3173
+ const nurbsData = this.extractNurbsFromEdge(edge);
3174
+ if (!nurbsData || nurbsData.controlPoints.length < 6) return null;
3175
+ const n9 = nurbsData.controlPoints.length;
3176
+ return [
3177
+ nurbsData.controlPoints[n9 - 6],
3178
+ nurbsData.controlPoints[n9 - 5],
3179
+ nurbsData.controlPoints[n9 - 4]
3180
+ ];
3181
+ }
3182
+ // ═══════════════════════════════════════════════════════════════════════
3183
+ // Surface geometry extraction
3184
+ // ═══════════════════════════════════════════════════════════════════════
3185
+ getSurfaceCylinderData(surface) {
3186
+ if (isBrepkitHandle(surface) && surface.type === "face") {
3187
+ const faceId = surface.id;
3188
+ const params = JSON.parse(this.bk.getAnalyticSurfaceParams(faceId));
3189
+ if (params.type === "cylinder") {
3190
+ return { radius: params.radius, isDirect: true };
3191
+ }
3192
+ }
3193
+ return null;
3194
+ }
3195
+ reverseSurfaceU(surface) {
3196
+ return surface;
3197
+ }
3198
+ // ═══════════════════════════════════════════════════════════════════════
3199
+ // 3D geometry primitive factories
3200
+ // ═══════════════════════════════════════════════════════════════════════
3201
+ createPoint3d(x7, y6, z6) {
3202
+ return { x: x7, y: y6, z: z6 };
3203
+ }
3204
+ createDirection3d(x7, y6, z6) {
3205
+ const len = Math.sqrt(x7 * x7 + y6 * y6 + z6 * z6);
3206
+ return { x: x7 / len, y: y6 / len, z: z6 / len };
3207
+ }
3208
+ createVector3d(x7, y6, z6) {
3209
+ return { x: x7, y: y6, z: z6 };
3210
+ }
3211
+ createAxis1(cx, cy, cz, dx, dy, dz) {
3212
+ return { origin: [cx, cy, cz], direction: [dx, dy, dz] };
3213
+ }
3214
+ createAxis2(ox, oy, oz, zx, zy, zz, xx, xy, xz) {
3215
+ return {
3216
+ origin: [ox, oy, oz],
3217
+ z: [zx, zy, zz],
3218
+ x: xx !== void 0 ? [xx, xy, xz] : void 0
3219
+ };
3220
+ }
3221
+ createAxis3(ox, oy, oz, zx, zy, zz, xx, xy, xz) {
3222
+ return {
3223
+ origin: [ox, oy, oz],
3224
+ z: [zx, zy, zz],
3225
+ x: xx !== void 0 ? [xx, xy, xz] : void 0
3226
+ };
3227
+ }
3228
+ // ═══════════════════════════════════════════════════════════════════════
3229
+ // Shape reversal
3230
+ // ═══════════════════════════════════════════════════════════════════════
3231
+ reverseShape(shape2) {
3232
+ const h8 = shape2;
3233
+ const newId = this.bk.reverseShape(h8.id);
3234
+ return handle(h8.type, newId);
3235
+ }
3236
+ // ═══════════════════════════════════════════════════════════════════════
3237
+ // Dispose
3238
+ // ═══════════════════════════════════════════════════════════════════════
3239
+ // TODO: Expose bk.checkpoint() / bk.restore() / bk.discardCheckpoint() for
3240
+ // transactional arena management. Could back a future undo/redo or speculative
3241
+ // operation API (try operation → restore on failure). See brepkitWasmTypes.ts.
3242
+ dispose(_handle) {
3243
+ }
3244
+ // ═══════════════════════════════════════════════════════════════════════
3245
+ // Kernel2DCapability (stubs — returns false from supportsKernel2D)
3246
+ // ═══════════════════════════════════════════════════════════════════════
3247
+ // ═══════════════════════════════════════════════════════════════════════
3248
+ // Kernel2DCapability — pure TypeScript implementation
3249
+ // ═══════════════════════════════════════════════════════════════════════
3250
+ c2d(h8) {
3251
+ return h8;
3252
+ }
3253
+ /** Unwrap any trimmed wrappers to get the underlying geometry. */
3254
+ c2dBasis(h8) {
3255
+ let c7 = this.c2d(h8);
3256
+ while (c7.__bk2d === "trimmed") c7 = c7.basis;
3257
+ return c7;
3258
+ }
3259
+ bb2d(h8) {
3260
+ return h8;
3261
+ }
3262
+ createPoint2d(x7, y6) {
3263
+ return { x: x7, y: y6 };
3264
+ }
3265
+ createDirection2d(x7, y6) {
3266
+ const l10 = Math.sqrt(x7 * x7 + y6 * y6);
3267
+ return { x: x7 / l10, y: y6 / l10 };
3268
+ }
3269
+ createVector2d(x7, y6) {
3270
+ return { x: x7, y: y6 };
3271
+ }
3272
+ createAxis2d(px, py, dx, dy) {
3273
+ return { px, py, dx, dy };
3274
+ }
3275
+ wrapCurve2dHandle(handle2) {
3276
+ return handle2;
3277
+ }
3278
+ createCurve2dAdaptor(handle2) {
3279
+ return handle2;
3280
+ }
3281
+ makeLine2d(x1, y1, x22, y22) {
3282
+ return makeLine2d(x1, y1, x22, y22);
3283
+ }
3284
+ makeCircle2d(cx, cy, radius, sense) {
3285
+ return makeCircle2d(cx, cy, radius, sense);
3286
+ }
3287
+ makeArc2dThreePoints(x1, y1, xm, ym, x22, y22) {
3288
+ const d10 = 2 * (x1 * (ym - y22) + xm * (y22 - y1) + x22 * (y1 - ym));
3289
+ if (Math.abs(d10) < 1e-12) {
3290
+ return makeLine2d(x1, y1, x22, y22);
3291
+ }
3292
+ const cx = ((x1 * x1 + y1 * y1) * (ym - y22) + (xm * xm + ym * ym) * (y22 - y1) + (x22 * x22 + y22 * y22) * (y1 - ym)) / d10;
3293
+ const cy = ((x1 * x1 + y1 * y1) * (x22 - xm) + (xm * xm + ym * ym) * (x1 - x22) + (x22 * x22 + y22 * y22) * (xm - x1)) / d10;
3294
+ const radius = Math.sqrt((x1 - cx) ** 2 + (y1 - cy) ** 2);
3295
+ const a12 = Math.atan2(y1 - cy, x1 - cx);
3296
+ const am = Math.atan2(ym - cy, xm - cx);
3297
+ const a23 = Math.atan2(y22 - cy, x22 - cx);
3298
+ let da1m = am - a12;
3299
+ if (da1m < 0) da1m += 2 * Math.PI;
3300
+ let da12 = a23 - a12;
3301
+ if (da12 < 0) da12 += 2 * Math.PI;
3302
+ const sense = da1m < da12;
3303
+ const circle = makeCircle2d(cx, cy, radius, sense);
3304
+ if (!sense) {
3305
+ return { __bk2d: "trimmed", basis: circle, tStart: -a12, tEnd: -a23 };
3306
+ }
3307
+ return { __bk2d: "trimmed", basis: circle, tStart: a12, tEnd: a23 };
3308
+ }
3309
+ makeArc2dTangent(sx, sy, tx, ty, ex, ey) {
3310
+ const len = Math.sqrt(tx * tx + ty * ty);
3311
+ const ntx = len > 0 ? tx / len : 0;
3312
+ const nty = len > 0 ? ty / len : 0;
3313
+ const chord = Math.sqrt((ex - sx) ** 2 + (ey - sy) ** 2);
3314
+ const offset2 = chord * 0.25;
3315
+ return this.makeArc2dThreePoints(
3316
+ sx,
3317
+ sy,
3318
+ (sx + ex) / 2 + nty * offset2,
3319
+ (sy + ey) / 2 - ntx * offset2,
3320
+ ex,
3321
+ ey
3322
+ );
3323
+ }
3324
+ makeEllipse2d(cx, cy, major, minor, xDirX, xDirY, sense) {
3325
+ return makeEllipse2d(cx, cy, major, minor, xDirX, xDirY, sense);
3326
+ }
3327
+ makeEllipseArc2d(cx, cy, major, minor, start, end, xDirX, xDirY, sense) {
3328
+ const ellipse = makeEllipse2d(cx, cy, major, minor, xDirX, xDirY, sense);
3329
+ return { __bk2d: "trimmed", basis: ellipse, tStart: start, tEnd: end };
3330
+ }
3331
+ makeBezier2d(points) {
3332
+ return makeBezier2d(points);
3333
+ }
3334
+ makeBSpline2d(points, _options) {
3335
+ const n9 = points.length;
3336
+ const degree = Math.min(3, n9 - 1);
3337
+ const knots = [];
3338
+ const mults = [];
3339
+ knots.push(0);
3340
+ mults.push(degree + 1);
3341
+ const nInternal = n9 - degree - 1;
3342
+ for (let i7 = 1; i7 <= nInternal; i7++) {
3343
+ knots.push(i7 / (nInternal + 1));
3344
+ mults.push(1);
3345
+ }
3346
+ knots.push(1);
3347
+ mults.push(degree + 1);
3348
+ return {
3349
+ __bk2d: "bspline",
3350
+ poles: [...points],
3351
+ knots,
3352
+ multiplicities: mults,
3353
+ degree,
3354
+ isPeriodic: false
3355
+ };
3356
+ }
3357
+ evaluateCurve2d(curve, param) {
3358
+ return evaluateCurve2d(this.c2d(curve), param);
3359
+ }
3360
+ evaluateCurve2dD1(curve, param) {
3361
+ return {
3362
+ point: evaluateCurve2d(this.c2d(curve), param),
3363
+ tangent: tangentCurve2d(this.c2d(curve), param)
3364
+ };
3365
+ }
3366
+ getCurve2dBounds(curve) {
3367
+ return curveBounds(this.c2d(curve));
3368
+ }
3369
+ getCurve2dType(curve) {
3370
+ return curveTypeName(this.c2dBasis(curve));
3371
+ }
3372
+ trimCurve2d(curve, start, end) {
3373
+ return { __bk2d: "trimmed", basis: this.c2d(curve), tStart: start, tEnd: end };
3374
+ }
3375
+ reverseCurve2d(_curve) {
3376
+ }
3377
+ copyCurve2d(curve) {
3378
+ return JSON.parse(JSON.stringify(curve));
3379
+ }
3380
+ offsetCurve2d(curve, offset2) {
3381
+ const c7 = this.c2d(curve);
3382
+ const bounds = curveBounds(c7);
3383
+ const N4 = 30;
3384
+ const poles = [];
3385
+ for (let i7 = 0; i7 <= N4; i7++) {
3386
+ const t11 = bounds.first + (bounds.last - bounds.first) * i7 / N4;
3387
+ const [px, py] = evaluateCurve2d(c7, t11);
3388
+ const [tx, ty] = tangentCurve2d(c7, t11);
3389
+ const tLen = Math.sqrt(tx * tx + ty * ty);
3390
+ if (tLen > 1e-12) {
3391
+ poles.push([px - ty / tLen * offset2, py + tx / tLen * offset2]);
3392
+ } else {
3393
+ poles.push([px, py]);
3394
+ }
3395
+ }
3396
+ return this.makeBSpline2d(poles);
3397
+ }
3398
+ translateCurve2d(curve, dx, dy) {
3399
+ return translateCurve2d(this.c2d(curve), dx, dy);
3400
+ }
3401
+ rotateCurve2d(curve, angle, cx, cy) {
3402
+ return rotateCurve2d(this.c2d(curve), angle, cx, cy);
3403
+ }
3404
+ scaleCurve2d(curve, factor, cx, cy) {
3405
+ return scaleCurve2d(this.c2d(curve), factor, cx, cy);
3406
+ }
3407
+ mirrorCurve2dAtPoint(curve, cx, cy) {
3408
+ return mirrorAtPoint(this.c2d(curve), cx, cy);
3409
+ }
3410
+ mirrorCurve2dAcrossAxis(curve, ox, oy, dx, dy) {
3411
+ return mirrorAcrossAxis(this.c2d(curve), ox, oy, dx, dy);
3412
+ }
3413
+ affinityTransform2d(curve, ox, oy, dx, dy, ratio) {
3414
+ const len = Math.sqrt(dx * dx + dy * dy);
3415
+ if (len < 1e-15) return this.c2d(curve);
3416
+ const ax = dx / len, ay = dy / len;
3417
+ const px = -ay, py = ax;
3418
+ const k7 = ratio - 1;
3419
+ const m00 = 1 + k7 * px * px, m01 = k7 * px * py;
3420
+ const m10 = k7 * py * px, m11 = 1 + k7 * py * py;
3421
+ const txOff = ox - m00 * ox - m01 * oy;
3422
+ const tyOff = oy - m10 * ox - m11 * oy;
3423
+ const gtrsf = { m: [m00, m01, 0, m10, m11, 0, 0, 0, 1], tx: txOff, ty: tyOff };
3424
+ return this.transformCurve2dGeneral(curve, gtrsf);
3425
+ }
3426
+ // --- General 2D transforms (stored as 3×3 matrices) ---
3427
+ createIdentityGTrsf2d() {
3428
+ return { m: [1, 0, 0, 0, 1, 0, 0, 0, 1], tx: 0, ty: 0 };
3429
+ }
3430
+ createAffinityGTrsf2d(ox, oy, dx, dy, ratio) {
3431
+ const len = Math.sqrt(dx * dx + dy * dy);
3432
+ if (len < 1e-15) return this.createIdentityGTrsf2d();
3433
+ const px = -dy / len, py = dx / len;
3434
+ const k7 = ratio - 1;
3435
+ const m10 = [1 + k7 * px * px, k7 * px * py, 0, k7 * py * px, 1 + k7 * py * py, 0, 0, 0, 1];
3436
+ const txv = ox - m10[0] * ox - m10[1] * oy;
3437
+ const tyv = oy - m10[3] * ox - m10[4] * oy;
3438
+ return { m: m10, tx: txv, ty: tyv };
3439
+ }
3440
+ createTranslationGTrsf2d(dx, dy) {
3441
+ return { m: [1, 0, 0, 0, 1, 0, 0, 0, 1], tx: dx, ty: dy };
3442
+ }
3443
+ createMirrorGTrsf2d(cx, cy, mode, ox, oy, dx, dy) {
3444
+ if (mode === "axis" && dx !== void 0 && dy !== void 0) {
3445
+ const len = Math.sqrt(dx * dx + dy * dy);
3446
+ const nx = dx / len, ny = dy / len;
3447
+ const m10 = [2 * nx * nx - 1, 2 * nx * ny, 0, 2 * nx * ny, 2 * ny * ny - 1, 0, 0, 0, 1];
3448
+ const px = ox ?? cx, py = oy ?? cy;
3449
+ const txv = px - m10[0] * px - m10[1] * py;
3450
+ const tyv = py - m10[3] * px - m10[4] * py;
3451
+ return { m: m10, tx: txv, ty: tyv };
3452
+ }
3453
+ return { m: [-1, 0, 0, 0, -1, 0, 0, 0, 1], tx: 2 * cx, ty: 2 * cy };
3454
+ }
3455
+ createRotationGTrsf2d(angle, cx, cy) {
3456
+ const c7 = Math.cos(angle), s6 = Math.sin(angle);
3457
+ return { m: [c7, -s6, 0, s6, c7, 0, 0, 0, 1], tx: cx - c7 * cx + s6 * cy, ty: cy - s6 * cx - c7 * cy };
3458
+ }
3459
+ createScaleGTrsf2d(factor, cx, cy) {
3460
+ return {
3461
+ m: [factor, 0, 0, 0, factor, 0, 0, 0, 1],
3462
+ tx: cx * (1 - factor),
3463
+ ty: cy * (1 - factor)
3464
+ };
3465
+ }
3466
+ setGTrsf2dTranslationPart(gtrsf, dx, dy) {
3467
+ gtrsf.tx = dx;
3468
+ gtrsf.ty = dy;
3469
+ }
3470
+ multiplyGTrsf2d(base, other) {
3471
+ const a9 = base.m, b10 = other.m;
3472
+ const r9 = [
3473
+ a9[0] * b10[0] + a9[1] * b10[3] + a9[2] * b10[6],
3474
+ a9[0] * b10[1] + a9[1] * b10[4] + a9[2] * b10[7],
3475
+ a9[0] * b10[2] + a9[1] * b10[5] + a9[2] * b10[8],
3476
+ a9[3] * b10[0] + a9[4] * b10[3] + a9[5] * b10[6],
3477
+ a9[3] * b10[1] + a9[4] * b10[4] + a9[5] * b10[7],
3478
+ a9[3] * b10[2] + a9[4] * b10[5] + a9[5] * b10[8],
3479
+ a9[6] * b10[0] + a9[7] * b10[3] + a9[8] * b10[6],
3480
+ a9[6] * b10[1] + a9[7] * b10[4] + a9[8] * b10[7],
3481
+ a9[6] * b10[2] + a9[7] * b10[5] + a9[8] * b10[8]
3482
+ ];
3483
+ base.m = r9;
3484
+ const oldTx = base.tx, oldTy = base.ty;
3485
+ const otx = Number(other.tx) || 0, oty = Number(other.ty) || 0;
3486
+ base.tx = a9[0] * otx + a9[1] * oty + oldTx;
3487
+ base.ty = a9[3] * otx + a9[4] * oty + oldTy;
3488
+ }
3489
+ transformCurve2dGeneral(curve, gtrsf) {
3490
+ const c7 = this.c2d(curve);
3491
+ const m10 = gtrsf.m ?? [1, 0, 0, 0, 1, 0, 0, 0, 1];
3492
+ const tx = Number(gtrsf.tx) || 0, ty = Number(gtrsf.ty) || 0;
3493
+ const isIdentityMatrix = Math.abs(m10[0] - 1) < 1e-12 && Math.abs(m10[4] - 1) < 1e-12 && Math.abs(m10[1]) < 1e-12 && Math.abs(m10[3]) < 1e-12;
3494
+ if (isIdentityMatrix) {
3495
+ return translateCurve2d(c7, tx, ty);
3496
+ }
3497
+ const bounds = curveBounds(c7);
3498
+ const N4 = 20;
3499
+ const pts = [];
3500
+ for (let i7 = 0; i7 <= N4; i7++) {
3501
+ const t11 = bounds.first + (bounds.last - bounds.first) * i7 / N4;
3502
+ const [px, py] = evaluateCurve2d(c7, t11);
3503
+ pts.push([m10[0] * px + m10[1] * py + tx, m10[3] * px + m10[4] * py + ty]);
3504
+ }
3505
+ return makeBezier2d(pts);
3506
+ }
3507
+ // --- 2D intersection & distance ---
3508
+ intersectCurves2d(c1, c22, tolerance) {
3509
+ const result = intersectCurves2dFn(this.c2d(c1), this.c2d(c22), tolerance);
3510
+ const segments = result.segments.map(
3511
+ (s6) => Object.assign(s6, {
3512
+ delete() {
3513
+ }
3514
+ })
3515
+ );
3516
+ return { points: result.points, segments };
3517
+ }
3518
+ projectPointOnCurve2d(curve, x7, y6) {
3519
+ const c7 = this.c2d(curve);
3520
+ const bounds = curveBounds(c7);
3521
+ if (c7.__bk2d === "line") {
3522
+ const dx = x7 - c7.ox;
3523
+ const dy = y6 - c7.oy;
3524
+ const t11 = Math.max(bounds.first, Math.min(bounds.last, dx * c7.dx + dy * c7.dy));
3525
+ const [px, py] = evaluateCurve2d(c7, t11);
3526
+ return { param: t11, distance: Math.sqrt((px - x7) ** 2 + (py - y6) ** 2) };
3527
+ }
3528
+ if (c7.__bk2d === "circle") {
3529
+ const angle = Math.atan2(y6 - c7.cy, x7 - c7.cx);
3530
+ let t11 = c7.sense ? angle : -angle;
3531
+ while (t11 < 0) t11 += 2 * Math.PI;
3532
+ while (t11 > 2 * Math.PI) t11 -= 2 * Math.PI;
3533
+ const [px, py] = evaluateCurve2d(c7, t11);
3534
+ return { param: t11, distance: Math.sqrt((px - x7) ** 2 + (py - y6) ** 2) };
3535
+ }
3536
+ if (!isFinite(bounds.first) || !isFinite(bounds.last)) return null;
3537
+ let bestT = bounds.first;
3538
+ let bestDist = Infinity;
3539
+ const N4 = 200;
3540
+ const dt = (bounds.last - bounds.first) / N4;
3541
+ for (let i7 = 0; i7 <= N4; i7++) {
3542
+ const t11 = bounds.first + i7 * dt;
3543
+ const [px, py] = evaluateCurve2d(c7, t11);
3544
+ const d10 = (px - x7) ** 2 + (py - y6) ** 2;
3545
+ if (d10 < bestDist) {
3546
+ bestDist = d10;
3547
+ bestT = t11;
3548
+ }
3549
+ }
3550
+ for (let iter = 0; iter < 10; iter++) {
3551
+ const [px, py] = evaluateCurve2d(c7, bestT);
3552
+ const [tx, ty] = tangentCurve2d(c7, bestT);
3553
+ const dot = (px - x7) * tx + (py - y6) * ty;
3554
+ const denom = tx * tx + ty * ty;
3555
+ if (denom < 1e-20) break;
3556
+ const step = dot / denom;
3557
+ const newT = Math.max(bounds.first, Math.min(bounds.last, bestT - step));
3558
+ if (Math.abs(newT - bestT) < 1e-14) break;
3559
+ bestT = newT;
3560
+ }
3561
+ const [fx, fy] = evaluateCurve2d(c7, bestT);
3562
+ return { param: bestT, distance: Math.sqrt((fx - x7) ** 2 + (fy - y6) ** 2) };
3563
+ }
3564
+ distanceBetweenCurves2d(c1, c22, p1s, p1e, p2s, p2e) {
3565
+ const curve1 = this.c2d(c1);
3566
+ const curve2 = this.c2d(c22);
3567
+ let bestT1 = p1s;
3568
+ let bestT2 = p2s;
3569
+ let minDistSq = Infinity;
3570
+ const N4 = 50;
3571
+ for (let i7 = 0; i7 <= N4; i7++) {
3572
+ const t12 = p1s + (p1e - p1s) * i7 / N4;
3573
+ const [x1, y1] = evaluateCurve2d(curve1, t12);
3574
+ for (let j6 = 0; j6 <= N4; j6++) {
3575
+ const t222 = p2s + (p2e - p2s) * j6 / N4;
3576
+ const [x22, y22] = evaluateCurve2d(curve2, t222);
3577
+ const d10 = (x22 - x1) ** 2 + (y22 - y1) ** 2;
3578
+ if (d10 < minDistSq) {
3579
+ minDistSq = d10;
3580
+ bestT1 = t12;
3581
+ bestT2 = t222;
3582
+ }
3583
+ }
3584
+ }
3585
+ let t1 = bestT1;
3586
+ let t22 = bestT2;
3587
+ for (let iter = 0; iter < 20; iter++) {
3588
+ const [x22, y22] = evaluateCurve2d(curve2, t22);
3589
+ const proj1 = this.projectPointOnCurve2d(c1, x22, y22);
3590
+ if (proj1) {
3591
+ const newT1 = Math.max(p1s, Math.min(p1e, proj1.param));
3592
+ const converged1 = Math.abs(newT1 - t1) < 1e-12;
3593
+ t1 = newT1;
3594
+ if (converged1) break;
3595
+ }
3596
+ const [x1, y1] = evaluateCurve2d(curve1, t1);
3597
+ const proj2 = this.projectPointOnCurve2d(c22, x1, y1);
3598
+ if (proj2) {
3599
+ const newT2 = Math.max(p2s, Math.min(p2e, proj2.param));
3600
+ const converged2 = Math.abs(newT2 - t22) < 1e-12;
3601
+ t22 = newT2;
3602
+ if (converged2) break;
3603
+ }
3604
+ }
3605
+ const [fx1, fy1] = evaluateCurve2d(curve1, t1);
3606
+ const [fx2, fy2] = evaluateCurve2d(curve2, t22);
3607
+ return Math.sqrt((fx2 - fx1) ** 2 + (fy2 - fy1) ** 2);
3608
+ }
3609
+ approximateCurve2dAsBSpline(curve, tol, cont, maxSeg) {
3610
+ const c7 = this.c2d(curve);
3611
+ const bounds = curveBounds(c7);
3612
+ const contDeg = cont === "C0" ? 1 : cont === "C1" ? 2 : cont === "C2" ? 3 : 4;
3613
+ const degree = Math.max(3, contDeg);
3614
+ let N4 = Math.max(100, maxSeg * 10);
3615
+ let poles = [];
3616
+ let maxErr = Infinity;
3617
+ for (let attempt = 0; attempt < 3 && maxErr > tol; attempt++) {
3618
+ poles = [];
3619
+ for (let i7 = 0; i7 <= N4; i7++) {
3620
+ const t11 = bounds.first + (bounds.last - bounds.first) * i7 / N4;
3621
+ poles.push(evaluateCurve2d(c7, t11));
3622
+ }
3623
+ maxErr = 0;
3624
+ for (let i7 = 0; i7 < N4; i7++) {
3625
+ const tMid = bounds.first + (bounds.last - bounds.first) * (i7 + 0.5) / N4;
3626
+ const [ex, ey] = evaluateCurve2d(c7, tMid);
3627
+ const p0 = poles[i7];
3628
+ const p1 = poles[i7 + 1];
3629
+ const mx = (p0[0] + p1[0]) / 2;
3630
+ const my = (p0[1] + p1[1]) / 2;
3631
+ const err2 = Math.sqrt((ex - mx) ** 2 + (ey - my) ** 2);
3632
+ if (err2 > maxErr) maxErr = err2;
3633
+ }
3634
+ if (maxErr > tol) N4 = Math.min(N4 * 2, 500);
3635
+ }
3636
+ return this.makeBSpline2d(poles, { degMax: degree });
3637
+ }
3638
+ decomposeBSpline2dToBeziers(curve) {
3639
+ const c7 = this.c2dBasis(curve);
3640
+ if (c7.__bk2d === "bezier") return [curve];
3641
+ if (c7.__bk2d !== "bspline") {
3642
+ const approx = this.approximateCurve2dAsBSpline(curve, 1e-6, "C2", 10);
3643
+ return this.decomposeBSpline2dToBeziers(approx);
3644
+ }
3645
+ const trimBounds = curveBounds(this.c2d(curve));
3646
+ const first = trimBounds.first;
3647
+ const last = trimBounds.last;
3648
+ const internalKnots = [];
3649
+ for (const k7 of c7.knots) {
3650
+ if (k7 > first + 1e-12 && k7 < last - 1e-12) internalKnots.push(k7);
3651
+ }
3652
+ const breakpoints = [first, ...internalKnots, last];
3653
+ const result = [];
3654
+ for (let i7 = 0; i7 < breakpoints.length - 1; i7++) {
3655
+ const t0 = breakpoints[i7];
3656
+ const t1 = breakpoints[i7 + 1];
3657
+ const span = t1 - t0;
3658
+ if (span < 1e-15) continue;
3659
+ const p0 = evaluateCurve2d(c7, t0);
3660
+ const p32 = evaluateCurve2d(c7, t1);
3661
+ const tan0 = tangentCurve2d(c7, t0);
3662
+ const tan3 = tangentCurve2d(c7, t1);
3663
+ const s6 = span / 3;
3664
+ const bezier = {
3665
+ __bk2d: "bezier",
3666
+ poles: [
3667
+ p0,
3668
+ [p0[0] + tan0[0] * s6, p0[1] + tan0[1] * s6],
3669
+ [p32[0] - tan3[0] * s6, p32[1] - tan3[1] * s6],
3670
+ p32
3671
+ ]
3672
+ };
3673
+ result.push(bezier);
3674
+ }
3675
+ return result.length > 0 ? result : [curve];
3676
+ }
3677
+ // --- 2D bounding boxes ---
3678
+ createBoundingBox2d() {
3679
+ return createBBox2d();
3680
+ }
3681
+ addCurveToBBox2d(bbox, curve, tol) {
3682
+ addCurveToBBox(this.bb2d(bbox), this.c2d(curve));
3683
+ }
3684
+ getBBox2dBounds(bbox) {
3685
+ const b10 = this.bb2d(bbox);
3686
+ return { xMin: b10.xMin, yMin: b10.yMin, xMax: b10.xMax, yMax: b10.yMax };
3687
+ }
3688
+ mergeBBox2d(target, other) {
3689
+ const t11 = this.bb2d(target), o9 = this.bb2d(other);
3690
+ t11.xMin = Math.min(t11.xMin, o9.xMin);
3691
+ t11.yMin = Math.min(t11.yMin, o9.yMin);
3692
+ t11.xMax = Math.max(t11.xMax, o9.xMax);
3693
+ t11.yMax = Math.max(t11.yMax, o9.yMax);
3694
+ }
3695
+ isBBox2dOut(a9, b10) {
3696
+ const ba = this.bb2d(a9), bb = this.bb2d(b10);
3697
+ return ba.xMax < bb.xMin || bb.xMax < ba.xMin || ba.yMax < bb.yMin || bb.yMax < ba.yMin;
3698
+ }
3699
+ isBBox2dOutPoint(bbox, x7, y6) {
3700
+ const b10 = this.bb2d(bbox);
3701
+ return x7 < b10.xMin || x7 > b10.xMax || y6 < b10.yMin || y6 > b10.yMax;
3702
+ }
3703
+ // --- 2D type extraction ---
3704
+ getCurve2dCircleData(curve) {
3705
+ const c7 = this.c2dBasis(curve);
3706
+ if (c7.__bk2d === "circle") return { cx: c7.cx, cy: c7.cy, radius: c7.radius, isDirect: c7.sense };
3707
+ return null;
3708
+ }
3709
+ getCurve2dEllipseData(curve) {
3710
+ const c7 = this.c2dBasis(curve);
3711
+ if (c7.__bk2d === "ellipse")
3712
+ return {
3713
+ majorRadius: c7.majorRadius,
3714
+ minorRadius: c7.minorRadius,
3715
+ xAxisAngle: c7.xDirAngle,
3716
+ isDirect: c7.sense
3717
+ };
3718
+ return null;
3719
+ }
3720
+ getCurve2dBezierPoles(curve) {
3721
+ const c7 = this.c2dBasis(curve);
3722
+ if (c7.__bk2d === "bezier") return [...c7.poles];
3723
+ return null;
3724
+ }
3725
+ getCurve2dBezierDegree(curve) {
3726
+ const c7 = this.c2dBasis(curve);
3727
+ if (c7.__bk2d === "bezier") return c7.poles.length - 1;
3728
+ return null;
3729
+ }
3730
+ getCurve2dBSplineData(curve) {
3731
+ const c7 = this.c2dBasis(curve);
3732
+ if (c7.__bk2d === "bspline")
3733
+ return {
3734
+ poles: [...c7.poles],
3735
+ knots: [...c7.knots],
3736
+ multiplicities: [...c7.multiplicities],
3737
+ degree: c7.degree,
3738
+ isPeriodic: c7.isPeriodic
3739
+ };
3740
+ return null;
3741
+ }
3742
+ // --- 2D serialization ---
3743
+ serializeCurve2d(curve) {
3744
+ return serializeCurve2d(this.c2d(curve));
3745
+ }
3746
+ deserializeCurve2d(data) {
3747
+ return deserializeCurve2d(data);
3748
+ }
3749
+ // --- 2D curve splitting ---
3750
+ splitCurve2d(curve, params) {
3751
+ const c7 = this.c2d(curve);
3752
+ const bounds = curveBounds(c7);
3753
+ const sortedParams = [bounds.first, ...params.sort((a9, b10) => a9 - b10), bounds.last];
3754
+ const result = [];
3755
+ for (let i7 = 0; i7 < sortedParams.length - 1; i7++) {
3756
+ result.push({
3757
+ __bk2d: "trimmed",
3758
+ basis: c7,
3759
+ tStart: sortedParams[i7],
3760
+ tEnd: sortedParams[i7 + 1]
3761
+ });
3762
+ }
3763
+ return result;
3764
+ }
3765
+ // --- 2D → 3D projection ---
3766
+ liftCurve2dToPlane(curve, origin, planeZ, planeX) {
3767
+ const c7 = this.c2d(curve);
3768
+ const y6 = [
3769
+ planeZ[1] * planeX[2] - planeZ[2] * planeX[1],
3770
+ planeZ[2] * planeX[0] - planeZ[0] * planeX[2],
3771
+ planeZ[0] * planeX[1] - planeZ[1] * planeX[0]
3772
+ ];
3773
+ const lift = (u7, v9) => [
3774
+ origin[0] + u7 * planeX[0] + v9 * y6[0],
3775
+ origin[1] + u7 * planeX[1] + v9 * y6[1],
3776
+ origin[2] + u7 * planeX[2] + v9 * y6[2]
3777
+ ];
3778
+ if (c7.__bk2d === "line") {
3779
+ const p1 = lift(c7.ox, c7.oy);
3780
+ const p22 = lift(c7.ox + c7.dx * c7.len, c7.oy + c7.dy * c7.len);
3781
+ return this.makeLineEdge(p1, p22);
3782
+ }
3783
+ if (c7.__bk2d === "circle" || c7.__bk2d === "trimmed") {
3784
+ const bounds2 = curveBounds(c7);
3785
+ let angularSpan;
3786
+ if (c7.__bk2d === "trimmed") {
3787
+ angularSpan = Math.abs(c7.tEnd - c7.tStart);
3788
+ } else {
3789
+ angularSpan = 2 * Math.PI;
3790
+ }
3791
+ const nSegments = angularSpan > Math.PI ? 4 : angularSpan > Math.PI / 2 ? 2 : 1;
3792
+ const segmentSpan = (bounds2.last - bounds2.first) / nSegments;
3793
+ const samplesPerSegment = Math.max(12, Math.ceil(angularSpan / nSegments / (Math.PI / 45)));
3794
+ if (nSegments === 1) {
3795
+ const points2 = [];
3796
+ for (let i7 = 0; i7 <= samplesPerSegment; i7++) {
3797
+ const t11 = bounds2.first + (bounds2.last - bounds2.first) * i7 / samplesPerSegment;
3798
+ const [u7, v9] = evaluateCurve2d(c7, t11);
3799
+ points2.push(lift(u7, v9));
3800
+ }
3801
+ return this.interpolatePoints(points2);
3802
+ }
3803
+ const edgeIds = [];
3804
+ for (let seg = 0; seg < nSegments; seg++) {
3805
+ const segStart = bounds2.first + seg * segmentSpan;
3806
+ const segEnd = bounds2.first + (seg + 1) * segmentSpan;
3807
+ const points2 = [];
3808
+ for (let i7 = 0; i7 <= samplesPerSegment; i7++) {
3809
+ const t11 = segStart + (segEnd - segStart) * i7 / samplesPerSegment;
3810
+ const [u7, v9] = evaluateCurve2d(c7, t11);
3811
+ points2.push(lift(u7, v9));
3812
+ }
3813
+ const coords = points2.flatMap(([px, py, pz]) => [px, py, pz]);
3814
+ const degree = Math.min(3, points2.length - 1);
3815
+ edgeIds.push(this.bk.interpolatePoints(coords, degree));
3816
+ }
3817
+ const wireId = this.bk.makeWire(edgeIds, false);
3818
+ return wireHandle(wireId);
3819
+ }
3820
+ if (c7.__bk2d === "bezier") {
3821
+ const points3d = c7.poles.map(([u7, v9]) => lift(u7, v9));
3822
+ if (points3d.length === 2) return this.makeLineEdge(points3d[0], points3d[1]);
3823
+ const degree = Math.min(3, points3d.length - 1);
3824
+ const coords = points3d.flatMap(([px, py, pz]) => [px, py, pz]);
3825
+ const id = this.bk.interpolatePoints(coords, degree);
3826
+ return edgeHandle(id);
3827
+ }
3828
+ if (c7.__bk2d === "bspline") {
3829
+ const points3d = c7.poles.map(([u7, v9]) => lift(u7, v9));
3830
+ if (points3d.length === 2) return this.makeLineEdge(points3d[0], points3d[1]);
3831
+ const degree = Math.min(3, points3d.length - 1);
3832
+ const coords = points3d.flatMap(([px, py, pz]) => [px, py, pz]);
3833
+ const id = this.bk.interpolatePoints(coords, degree);
3834
+ return edgeHandle(id);
3835
+ }
3836
+ const bounds = curveBounds(c7);
3837
+ const nSamples = 100;
3838
+ const points = [];
3839
+ for (let i7 = 0; i7 <= nSamples; i7++) {
3840
+ const t11 = bounds.first + (bounds.last - bounds.first) * i7 / nSamples;
3841
+ const [u7, v9] = evaluateCurve2d(c7, t11);
3842
+ points.push(lift(u7, v9));
3843
+ }
3844
+ return this.interpolatePoints(points);
3845
+ }
3846
+ buildEdgeOnSurface(curve, surface) {
3847
+ if (!isBrepkitHandle(surface))
3848
+ throw new Error("brepkit: buildEdgeOnSurface requires a face handle as surface");
3849
+ const fid = unwrap(surface, "face");
3850
+ const c7 = this.c2d(curve);
3851
+ const bounds = curveBounds(c7);
3852
+ const surfType = this.bk.getSurfaceType(fid);
3853
+ const N4 = surfType === "plane" ? 50 : 100;
3854
+ const points = [];
3855
+ for (let i7 = 0; i7 <= N4; i7++) {
3856
+ const t11 = bounds.first + (bounds.last - bounds.first) * i7 / N4;
3857
+ const [u7, v9] = evaluateCurve2d(c7, t11);
3858
+ const p7 = this.bk.evaluateSurface(fid, u7, v9);
3859
+ points.push([p7[0], p7[1], p7[2]]);
3860
+ }
3861
+ return this.interpolatePoints(points);
3862
+ }
3863
+ extractSurfaceFromFace(face) {
3864
+ return face;
3865
+ }
3866
+ extractCurve2dFromEdge(edge, face) {
3867
+ const eid = unwrap(edge, "edge");
3868
+ unwrap(face, "face");
3869
+ const params = this.bk.getEdgeCurveParameters(eid);
3870
+ const tMin = params[0] ?? 0;
3871
+ const tMax = params[1] ?? 1;
3872
+ const N4 = 40;
3873
+ const uvPoints = [];
3874
+ for (let i7 = 0; i7 <= N4; i7++) {
3875
+ const t11 = tMin + (tMax - tMin) * i7 / N4;
3876
+ const pt = this.bk.evaluateEdgeCurve(eid, t11);
3877
+ uvPoints.push([pt[0], pt[1]]);
3878
+ }
3879
+ if (uvPoints.length >= 2) {
3880
+ return this.makeBSpline2d(uvPoints);
3881
+ }
3882
+ const verts = this.bk.getEdgeVertices(eid);
3883
+ return makeLine2d(verts[0], verts[1], verts[3], verts[4]);
3884
+ }
3885
+ buildCurves3d(_wire) {
3886
+ }
3887
+ fixWireOnFace(wire, _face, _tolerance) {
3888
+ return wire;
3889
+ }
3890
+ fillSurface(wires, _options) {
3891
+ if (wires.length >= 1) {
3892
+ const wireEdges = this.iterShapes(wires[0], "edge");
3893
+ if (wireEdges.length === 4) {
3894
+ const allCoords = [];
3895
+ const curveLengths = [];
3896
+ for (const edge of wireEdges) {
3897
+ const edgeId = unwrap(edge, "edge");
3898
+ const params = this.bk.getEdgeCurveParameters(edgeId);
3899
+ const tMin = params[0], tMax = params[1];
3900
+ const N4 = 10;
3901
+ const pts = [];
3902
+ for (let i7 = 0; i7 <= N4; i7++) {
3903
+ const t11 = tMin + (tMax - tMin) * i7 / N4;
3904
+ const p7 = this.bk.evaluateEdgeCurve(edgeId, t11);
3905
+ pts.push(p7[0], p7[1], p7[2]);
3906
+ }
3907
+ allCoords.push(...pts);
3908
+ curveLengths.push(N4 + 1);
3909
+ }
3910
+ try {
3911
+ const faceId = this.bk.fillCoonsPatch(allCoords, curveLengths);
3912
+ return faceHandle(faceId);
3913
+ } catch (e8) {
3914
+ console.warn("brepkit: Coons patch failed, falling back:", e8);
3915
+ }
3916
+ }
3917
+ }
3918
+ const outerWire2 = wires[0];
3919
+ if (!outerWire2) throw new Error("fillSurface: no wires provided");
3920
+ return this.makeNonPlanarFace(outerWire2);
3921
+ }
3922
+ // ═══════════════════════════════════════════════════════════════════════
3923
+ // Private helpers
3924
+ // ═══════════════════════════════════════════════════════════════════════
3925
+ applyMatrix(shape2, matrix) {
3926
+ const h8 = shape2;
3927
+ if (!isBrepkitHandle(shape2)) {
3928
+ throw new Error("brepkit: applyMatrix requires a BrepkitHandle");
3929
+ }
3930
+ switch (h8.type) {
3931
+ case "solid": {
3932
+ const copy = this.bk.copySolid(h8.id);
3933
+ this.bk.transformSolid(copy, matrix);
3934
+ return solidHandle(copy);
3935
+ }
3936
+ case "face": {
3937
+ if (typeof this.bk.copyFace !== "function" || typeof this.bk.transformFace !== "function") {
3938
+ throw new Error(
3939
+ "brepkit: applyMatrix for faces requires copyFace/transformFace WASM exports"
3940
+ );
3941
+ }
3942
+ const copy = this.bk.copyFace(h8.id);
3943
+ this.bk.transformFace(copy, matrix);
3944
+ return faceHandle(copy);
3945
+ }
3946
+ case "wire": {
3947
+ if (typeof this.bk.copyWire !== "function" || typeof this.bk.transformWire !== "function") {
3948
+ throw new Error(
3949
+ "brepkit: applyMatrix for wires requires copyWire/transformWire WASM exports"
3950
+ );
3951
+ }
3952
+ const copy = this.bk.copyWire(h8.id);
3953
+ this.bk.transformWire(copy, matrix);
3954
+ return wireHandle(copy);
3955
+ }
3956
+ case "edge": {
3957
+ if (typeof this.bk.copyEdge !== "function" || typeof this.bk.transformEdge !== "function") {
3958
+ throw new Error(
3959
+ "brepkit: applyMatrix for edges requires copyEdge/transformEdge WASM exports"
3960
+ );
3961
+ }
3962
+ const copy = this.bk.copyEdge(h8.id);
3963
+ this.bk.transformEdge(copy, matrix);
3964
+ return edgeHandle(copy);
3965
+ }
3966
+ default:
3967
+ throw new Error(`brepkit: applyMatrix does not support '${h8.type}' shapes`);
3968
+ }
3969
+ }
3970
+ /** Check if we need to transform from default placement (origin, +Z). */
3971
+ needsTransform(center, direction) {
3972
+ if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) return true;
3973
+ if (direction && (direction[0] !== 0 || direction[1] !== 0 || direction[2] !== 1)) return true;
3974
+ return false;
3975
+ }
3976
+ /** Transform a shape from default placement (origin, +Z) to the given center and direction. */
3977
+ transformToPlacement(shape2, center, direction) {
3978
+ let result = shape2;
3979
+ if (direction && (direction[0] !== 0 || direction[1] !== 0 || direction[2] !== 1)) {
3980
+ const [dx, dy, dz] = direction;
3981
+ const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
3982
+ const nx = dx / len;
3983
+ const ny = dy / len;
3984
+ const nz = dz / len;
3985
+ const dot = nz;
3986
+ if (Math.abs(dot + 1) < 1e-10) {
3987
+ result = this.rotate(result, 180, [1, 0, 0]);
3988
+ } else if (Math.abs(dot - 1) > 1e-10) {
3989
+ const axis = [-ny, nx, 0];
3990
+ const angleDeg = Math.acos(Math.max(-1, Math.min(1, dot))) * (180 / Math.PI);
3991
+ result = this.rotate(result, angleDeg, axis);
3992
+ }
3993
+ }
3994
+ if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
3995
+ result = this.translate(result, center[0], center[1], center[2]);
3996
+ }
3997
+ return result;
3998
+ }
3999
+ /** Tessellate a solid with per-face groups for brepjs mesh format. */
4000
+ // TODO: Replace per-face tessellation loop with bk.tessellateSolidGrouped()
4001
+ // for a single WASM call instead of N. Requires aligning the grouped output
4002
+ // format with KernelMeshResult (faceGroups, index offsets). See brepkitWasmTypes.ts.
4003
+ meshSolid(solidId, deflection) {
4004
+ const faceIds = toArray(this.bk.getSolidFaces(solidId));
4005
+ const allVertices = [];
4006
+ const allNormals = [];
4007
+ const allTriangles = [];
4008
+ const allUVs = [];
4009
+ const faceGroups = [];
4010
+ let vertexOffset = 0;
4011
+ for (const faceId of faceIds) {
4012
+ try {
4013
+ const faceMesh = this.bk.tessellateFace(faceId, deflection);
4014
+ const positions = faceMesh.positions;
4015
+ const normals = faceMesh.normals;
4016
+ const indices = faceMesh.indices;
4017
+ const vertCount = positions.length / 3;
4018
+ if (vertCount === 0) continue;
4019
+ const triStart = allTriangles.length;
4020
+ for (const v9 of positions) allVertices.push(v9);
4021
+ for (const n9 of normals) allNormals.push(n9);
4022
+ for (const idx of indices) {
4023
+ allTriangles.push(idx + vertexOffset);
4024
+ }
4025
+ for (let i7 = 0; i7 < vertCount; i7++) {
4026
+ allUVs.push(0, 0);
4027
+ }
4028
+ faceGroups.push({
4029
+ start: triStart,
4030
+ count: indices.length,
4031
+ faceHash: faceId
4032
+ });
4033
+ vertexOffset += vertCount;
4034
+ } catch (e8) {
4035
+ console.warn(`brepkit: face tessellation failed (faceId=${faceId}):`, e8);
4036
+ }
4037
+ }
4038
+ return {
4039
+ vertices: new Float32Array(allVertices),
4040
+ normals: new Float32Array(allNormals),
4041
+ triangles: new Uint32Array(allTriangles),
4042
+ uvs: new Float32Array(allUVs),
4043
+ faceGroups
4044
+ };
4045
+ }
4046
+ /** Tessellate a single face and return brepjs mesh format. */
4047
+ meshSingleFace(faceId, deflection, faceHash) {
4048
+ const faceMesh = this.bk.tessellateFace(faceId, deflection);
4049
+ const positions = faceMesh.positions;
4050
+ const normals = faceMesh.normals;
4051
+ const indices = faceMesh.indices;
4052
+ const vertCount = positions.length / 3;
4053
+ const uvs = [];
4054
+ for (let i7 = 0; i7 < vertCount; i7++) {
4055
+ uvs.push(0, 0);
4056
+ }
4057
+ return {
4058
+ vertices: new Float32Array(positions),
4059
+ normals: new Float32Array(normals),
4060
+ triangles: new Uint32Array(indices),
4061
+ uvs: new Float32Array(uvs),
4062
+ faceGroups: [{ start: 0, count: indices.length, faceHash }]
4063
+ };
4064
+ }
4065
+ /**
4066
+ * Create a NURBS circle/arc edge in 3D.
4067
+ *
4068
+ * Uses the rational quadratic B-spline circle representation:
4069
+ * 9-point circle for full 2π, fewer arcs for partial.
4070
+ */
4071
+ makeCircleNurbs(center, normal, radius, startAngle, endAngle) {
4072
+ const len = Math.sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2);
4073
+ const nz = [normal[0] / len, normal[1] / len, normal[2] / len];
4074
+ const ref = Math.abs(nz[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
4075
+ const xAxis = [
4076
+ nz[1] * ref[2] - nz[2] * ref[1],
4077
+ nz[2] * ref[0] - nz[0] * ref[2],
4078
+ nz[0] * ref[1] - nz[1] * ref[0]
4079
+ ];
4080
+ const xLen = Math.sqrt(xAxis[0] ** 2 + xAxis[1] ** 2 + xAxis[2] ** 2);
4081
+ xAxis[0] /= xLen;
4082
+ xAxis[1] /= xLen;
4083
+ xAxis[2] /= xLen;
4084
+ const yAxis = [
4085
+ nz[1] * xAxis[2] - nz[2] * xAxis[1],
4086
+ nz[2] * xAxis[0] - nz[0] * xAxis[2],
4087
+ nz[0] * xAxis[1] - nz[1] * xAxis[0]
4088
+ ];
4089
+ const nSegments = Math.ceil(Math.abs(endAngle - startAngle) / (Math.PI / 2));
4090
+ const dAngle = (endAngle - startAngle) / nSegments;
4091
+ const controlPoints = [];
4092
+ const weights = [];
4093
+ for (let i7 = 0; i7 <= nSegments; i7++) {
4094
+ const angle = startAngle + i7 * dAngle;
4095
+ const cos = Math.cos(angle);
4096
+ const sin = Math.sin(angle);
4097
+ const px = center[0] + radius * (cos * xAxis[0] + sin * yAxis[0]);
4098
+ const py = center[1] + radius * (cos * xAxis[1] + sin * yAxis[1]);
4099
+ const pz = center[2] + radius * (cos * xAxis[2] + sin * yAxis[2]);
4100
+ if (i7 > 0) {
4101
+ const midAngle = startAngle + (i7 - 0.5) * dAngle;
4102
+ const midCos = Math.cos(midAngle);
4103
+ const midSin = Math.sin(midAngle);
4104
+ const midR = radius / Math.cos(dAngle / 2);
4105
+ const mx = center[0] + midR * (midCos * xAxis[0] + midSin * yAxis[0]);
4106
+ const my = center[1] + midR * (midCos * xAxis[1] + midSin * yAxis[1]);
4107
+ const mz = center[2] + midR * (midCos * xAxis[2] + midSin * yAxis[2]);
4108
+ controlPoints.push(mx, my, mz);
4109
+ weights.push(Math.cos(dAngle / 2));
4110
+ }
4111
+ controlPoints.push(px, py, pz);
4112
+ weights.push(1);
4113
+ }
4114
+ const degree = 2;
4115
+ const knots = Array(degree + 1).fill(0);
4116
+ for (let i7 = 1; i7 < nSegments; i7++) {
4117
+ knots.push(i7, i7);
4118
+ }
4119
+ knots.push(...Array(degree + 1).fill(nSegments));
4120
+ const kMax = knots[knots.length - 1];
4121
+ for (let i7 = 0; i7 < knots.length; i7++) {
4122
+ knots[i7] = knots[i7] / kMax;
4123
+ }
4124
+ const startPt = controlPoints.slice(0, 3);
4125
+ const endPt = controlPoints.slice(-3);
4126
+ const id = this.bk.makeNurbsEdge(
4127
+ startPt[0],
4128
+ startPt[1],
4129
+ startPt[2],
4130
+ endPt[0],
4131
+ endPt[1],
4132
+ endPt[2],
4133
+ degree,
4134
+ knots,
4135
+ controlPoints,
4136
+ weights
4137
+ );
4138
+ return edgeHandle(id);
4139
+ }
4140
+ /**
4141
+ * Extract NURBS curve data from an edge handle.
4142
+ * Returns null for line edges (caller can build a linear NURBS).
4143
+ * Returns {degree, knots, controlPoints, weights} for NURBS edges.
4144
+ */
4145
+ extractNurbsFromEdge(shape2) {
4146
+ const h8 = shape2;
4147
+ if (h8.type !== "edge") return null;
4148
+ const nurbsJson = this.bk.getEdgeNurbsData(h8.id);
4149
+ if (nurbsJson) {
4150
+ const data = JSON.parse(nurbsJson);
4151
+ return {
4152
+ degree: data.degree,
4153
+ knots: data.knots,
4154
+ controlPoints: data.controlPoints,
4155
+ weights: data.weights
4156
+ };
4157
+ }
4158
+ const verts = this.bk.getEdgeVertices(h8.id);
4159
+ return {
4160
+ degree: 1,
4161
+ knots: [0, 0, 1, 1],
4162
+ controlPoints: [verts[0], verts[1], verts[2], verts[3], verts[4], verts[5]],
4163
+ weights: [1, 1]
4164
+ };
4165
+ }
4166
+ /**
4167
+ * Create a NURBS ellipse/ellipse-arc edge in 3D.
4168
+ */
4169
+ makeEllipseNurbs(center, normal, majorRadius, minorRadius, startAngle, endAngle, xDir) {
4170
+ const len = Math.sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2);
4171
+ const nz = [normal[0] / len, normal[1] / len, normal[2] / len];
4172
+ let xAxis;
4173
+ if (xDir) {
4174
+ const xl = Math.sqrt(xDir[0] ** 2 + xDir[1] ** 2 + xDir[2] ** 2);
4175
+ xAxis = [xDir[0] / xl, xDir[1] / xl, xDir[2] / xl];
4176
+ } else {
4177
+ const ref = Math.abs(nz[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
4178
+ xAxis = [
4179
+ nz[1] * ref[2] - nz[2] * ref[1],
4180
+ nz[2] * ref[0] - nz[0] * ref[2],
4181
+ nz[0] * ref[1] - nz[1] * ref[0]
4182
+ ];
4183
+ const xLen2 = Math.sqrt(xAxis[0] ** 2 + xAxis[1] ** 2 + xAxis[2] ** 2);
4184
+ xAxis[0] /= xLen2;
4185
+ xAxis[1] /= xLen2;
4186
+ xAxis[2] /= xLen2;
4187
+ }
4188
+ const yAxis = [
4189
+ nz[1] * xAxis[2] - nz[2] * xAxis[1],
4190
+ nz[2] * xAxis[0] - nz[0] * xAxis[2],
4191
+ nz[0] * xAxis[1] - nz[1] * xAxis[0]
4192
+ ];
4193
+ const nSegments = Math.ceil(Math.abs(endAngle - startAngle) / (Math.PI / 2));
4194
+ const dAngle = (endAngle - startAngle) / nSegments;
4195
+ const controlPoints = [];
4196
+ const weights = [];
4197
+ for (let i7 = 0; i7 <= nSegments; i7++) {
4198
+ const angle = startAngle + i7 * dAngle;
4199
+ const cos = Math.cos(angle);
4200
+ const sin = Math.sin(angle);
4201
+ const px = center[0] + majorRadius * cos * xAxis[0] + minorRadius * sin * yAxis[0];
4202
+ const py = center[1] + majorRadius * cos * xAxis[1] + minorRadius * sin * yAxis[1];
4203
+ const pz = center[2] + majorRadius * cos * xAxis[2] + minorRadius * sin * yAxis[2];
4204
+ if (i7 > 0) {
4205
+ const midAngle = startAngle + (i7 - 0.5) * dAngle;
4206
+ const midCos = Math.cos(midAngle);
4207
+ const midSin = Math.sin(midAngle);
4208
+ const scale2 = 1 / Math.cos(dAngle / 2);
4209
+ const mx = center[0] + majorRadius * scale2 * midCos * xAxis[0] + minorRadius * scale2 * midSin * yAxis[0];
4210
+ const my = center[1] + majorRadius * scale2 * midCos * xAxis[1] + minorRadius * scale2 * midSin * yAxis[1];
4211
+ const mz = center[2] + majorRadius * scale2 * midCos * xAxis[2] + minorRadius * scale2 * midSin * yAxis[2];
4212
+ controlPoints.push(mx, my, mz);
4213
+ weights.push(Math.cos(dAngle / 2));
4214
+ }
4215
+ controlPoints.push(px, py, pz);
4216
+ weights.push(1);
4217
+ }
4218
+ const degree = 2;
4219
+ const knots = Array(degree + 1).fill(0);
4220
+ for (let i7 = 1; i7 < nSegments; i7++) {
4221
+ knots.push(i7, i7);
4222
+ }
4223
+ knots.push(...Array(degree + 1).fill(nSegments));
4224
+ const kMax = knots[knots.length - 1];
4225
+ for (let i7 = 0; i7 < knots.length; i7++) {
4226
+ knots[i7] = knots[i7] / kMax;
4227
+ }
4228
+ const startPt = controlPoints.slice(0, 3);
4229
+ const endPt = controlPoints.slice(-3);
4230
+ const id = this.bk.makeNurbsEdge(
4231
+ startPt[0],
4232
+ startPt[1],
4233
+ startPt[2],
4234
+ endPt[0],
4235
+ endPt[1],
4236
+ endPt[2],
4237
+ degree,
4238
+ knots,
4239
+ controlPoints,
4240
+ weights
4241
+ );
4242
+ return edgeHandle(id);
4243
+ }
4244
+ /**
4245
+ * Extract a plane definition (point + normal) from a face handle.
4246
+ * Uses tessellation to find a concrete point on the face.
4247
+ */
4248
+ extractPlaneFromFace(faceShape) {
4249
+ let faceId;
4250
+ const h8 = faceShape;
4251
+ if (h8.type === "solid" || h8.type === "compound") {
4252
+ const faces = this.iterShapes(faceShape, "face");
4253
+ if (faces.length === 0) throw new Error("brepkit: extractPlaneFromFace: no faces found");
4254
+ const firstFace = faces[0];
4255
+ if (!firstFace) throw new Error("brepkit: extractPlaneFromFace: no faces found");
4256
+ let bestId = unwrap(firstFace, "face");
4257
+ let bestArea = 0;
4258
+ for (const f12 of faces) {
4259
+ const id = unwrap(f12, "face");
4260
+ try {
4261
+ const a9 = this.bk.faceArea(id, DEFAULT_DEFLECTION);
4262
+ if (a9 > bestArea) {
4263
+ bestArea = a9;
4264
+ bestId = id;
4265
+ }
4266
+ } catch {
4267
+ }
4268
+ }
4269
+ faceId = bestId;
4270
+ } else {
4271
+ faceId = unwrap(faceShape, "face");
4272
+ }
4273
+ const n9 = this.bk.getFaceNormal(faceId);
4274
+ const normal = [n9[0], n9[1], n9[2]];
4275
+ const mesh2 = this.bk.tessellateFace(faceId, 1);
4276
+ const positions = mesh2.positions;
4277
+ if (positions.length >= 3) {
4278
+ return { point: [positions[0], positions[1], positions[2]], normal };
4279
+ }
4280
+ return { point: [0, 0, 0], normal };
4281
+ }
4282
+ }
4283
+ function multiplyMatrices(a9, b10) {
4284
+ const result = new Array(16).fill(0);
4285
+ for (let i7 = 0; i7 < 4; i7++) {
4286
+ for (let j6 = 0; j6 < 4; j6++) {
4287
+ for (let k7 = 0; k7 < 4; k7++) {
4288
+ result[i7 * 4 + j6] = result[i7 * 4 + j6] + a9[i7 * 4 + k7] * b10[k7 * 4 + j6];
4289
+ }
4290
+ }
4291
+ }
4292
+ return result;
4293
+ }
89
4294
  const errorFactories = {
90
4295
  KERNEL_OPERATION: (code, message, cause) => ({ kind: "KERNEL_OPERATION", code, message, cause }),
91
4296
  VALIDATION: (code, message, cause) => ({ kind: "VALIDATION", code, message, cause }),
@@ -1438,7 +5643,7 @@ function checkAllInterferences(shapes, tolerance = 1e-6) {
1438
5643
  shapes.forEach((si, i7) => {
1439
5644
  for (let j6 = i7 + 1; j6 < shapes.length; j6++) {
1440
5645
  if (aabbDisjoint(boxes[i7], boxes[j6], tolerance)) continue;
1441
- const result = unwrap(checkInterference(si, shapes[j6], tolerance));
5646
+ const result = unwrap$1(checkInterference(si, shapes[j6], tolerance));
1442
5647
  if (result.hasInterference) {
1443
5648
  pairs.push({ i: i7, j: j6, result });
1444
5649
  }
@@ -2617,6 +6822,7 @@ export {
2617
6822
  BrepBugError,
2618
6823
  BrepErrorCode,
2619
6824
  BrepWrapperError,
6825
+ BrepkitAdapter,
2620
6826
  C5 as CompoundBlueprint,
2621
6827
  C6 as CompoundSketch,
2622
6828
  C3 as Curve2D,
@@ -3031,7 +7237,7 @@ export {
3031
7237
  typeCastError,
3032
7238
  F2 as undoLast,
3033
7239
  u as unsupportedError,
3034
- unwrap,
7240
+ unwrap$1 as unwrap,
3035
7241
  v2 as unwrapErr,
3036
7242
  w2 as unwrapOr,
3037
7243
  x2 as unwrapOrElse,