brepjs 18.84.1 → 18.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,14 +38,14 @@ const step = unwrap(exportSTEP(part));
38
38
 
39
39
  brepjs grew out of the love and care I put into [gridfinitylayouttool.com](https://gridfinitylayouttool.com). I needed parametric CAD in the browser and I'm not a 3D modeler, but I know TypeScript. [OpenSCAD](https://openscad.org/) nailed code-first CAD but lives outside the JS ecosystem. [replicad](https://replicad.xyz/) proved OpenCascade works in JS but I kept hitting performance walls and fighting the API.
40
40
 
41
- Neither had the type safety I wanted, so brepjs leans hard on it: branded types, `Result<T,E>`, phantom types that prove invariants at compile time. If it compiles, the geometry is valid. Best for parts defined by parameters (enclosures, brackets, fixtures, gridfinity bins) rather than organic sculpting.
41
+ Neither had the type safety I wanted, so brepjs leans hard on it: branded types, `Result<T,E>`, phantom types that prove invariants at compile time. If it compiles, the geometry is valid. It's strongest at exact, manufacturable geometry — precise booleans, fillets, chamfers and shells; real volumes, areas and clearances; watertight solids that round-trip through STEP — from a single part to a full assembly (enclosures, brackets, fixtures, gridfinity bins, machined and molded parts). It isn't built for organic sculpting or dense lattices; that's what field-based (implicit/voxel) modeling is for.
42
42
 
43
43
  ## Scope
44
44
 
45
45
  To set expectations, this project deliberately does not:
46
46
 
47
47
  - **Render or display geometry**: brepjs produces shape data; pass mesh output to Three.js, Babylon.js, or raw WebGL for rendering.
48
- - **Support organic or sculpting workflows**: the API is built for parametric parts (enclosures, brackets, fixtures); freeform sculpting is out of scope.
48
+ - **Support organic or sculpting workflows**: brepjs models exact mechanical solids parts and assemblies; freeform/organic sculpting and dense lattices are out of scope (that's field-based implicit/voxel territory).
49
49
  - **Output SVG or 2D files**: 2D drawing primitives exist solely as an intermediate step toward extruded 3D solids, not as a standalone 2D output format.
50
50
  - **Run server-side (SSR)**: WASM requires a browser or Node.js environment with WASM support; server-side rendering frameworks (Next.js, Nuxt, Remix) need a client-only import.
51
51
  - **Provide a GUI**: brepjs is a pure programmatic API; there is no visual editor, viewport, or file picker.
@@ -105,7 +105,7 @@ Imports flow downward only. Boundaries are enforced in CI.
105
105
 
106
106
  ## Authoring CAD with AI (brepjs-verify)
107
107
 
108
- [`brepjs-verify`](https://www.npmjs.com/package/brepjs-verify) helps an AI agent (or you) author parametric CAD in brepjs and **prove it is correct** before handing it off. An LLM can't see geometry, so it writes a `.brep.ts` part, runs it on a real kernel, and reads a deterministic report instead of guessing from how the code reads. It ships as two cooperating pieces: a **Claude Code skill** (the authoring loop) and a **verification CLI** (validity + measured dimensions + multi-view snapshots + STEP export).
108
+ [`brepjs-verify`](https://www.npmjs.com/package/brepjs-verify) helps an AI agent (or you) author CAD in brepjs and **prove it is correct** before handing it off. An LLM can't see geometry, so it writes a `.brep.ts` part, runs it on a real kernel, and reads a deterministic report instead of guessing from how the code reads. It ships as two cooperating pieces: a **Claude Code skill** (the authoring loop) and a **verification CLI** (validity + measured dimensions + multi-view snapshots + STEP export).
109
109
 
110
110
  Install both; they ride on two rails:
111
111
 
@@ -141,6 +141,16 @@ The command exits non-zero unless the report is `ok` (valid **and** every declar
141
141
 
142
142
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
143
143
 
144
+ ## Acknowledgements
145
+
146
+ brepjs.dev and the playground are hosted on [Vercel](https://vercel.com) through its Open Source Program.
147
+
148
+ <div align="center">
149
+ <a href="https://vercel.com/open-source-program">
150
+ <img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge-2026.svg" />
151
+ </a>
152
+ </div>
153
+
144
154
  ## License
145
155
 
146
156
  [Apache-2.0](./LICENSE)
package/dist/brepjs.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_textBlueprints = require("./textBlueprints-BhxAXpVE.cjs");
2
+ const require_textBlueprints = require("./textBlueprints-CKFmYJHD.cjs");
3
3
  const require_shapeTypes = require("./shapeTypes-DwNTiXjG.cjs");
4
4
  const require_occtWasmAdapter = require("./occtWasmAdapter-Bv2OTOX9.cjs");
5
5
  const require_vec3 = require("./vec3-CFwOI0ZI.cjs");
@@ -20,7 +20,7 @@ const require_healingFns = require("./healingFns-B1axWCr3.cjs");
20
20
  const require_threadFns = require("./threadFns-BsRDNoSm.cjs");
21
21
  const require_blueprintSketcher = require("./blueprintSketcher-ChpPo7tq.cjs");
22
22
  const require_helpers = require("./helpers-Cvrmxw4s.cjs");
23
- const require_drawFns = require("./drawFns-DWHuHfHQ.cjs");
23
+ const require_drawFns = require("./drawFns-C766Umy1.cjs");
24
24
  const require_measureFns = require("./measureFns-BtPAmRfH.cjs");
25
25
  const require_cornerFinder = require("./cornerFinder-DKyR_u8g.cjs");
26
26
  const require_boolean2D = require("./boolean2D-RC6jXv4x.cjs");
@@ -28,7 +28,7 @@ const require_blueprintFns = require("./blueprintFns-Dp0_BOuL.cjs");
28
28
  const require_importFns = require("./importFns-D09lb1dB.cjs");
29
29
  const require_loftFns = require("./loftFns-C664R57e.cjs");
30
30
  const require_cameraFns = require("./cameraFns-IL7qYFaG.cjs");
31
- const require_textMetrics = require("./textMetrics-SaXWFfLN.cjs");
31
+ const require_textMetrics = require("./textMetrics-BYHtOFYW.cjs");
32
32
  const require_shapeRefFns = require("./shapeRefFns-BsX2j-n3.cjs");
33
33
  const require_workerHandler = require("./workerHandler-CdlOTwJg.cjs");
34
34
  const require_primitiveFns = require("./primitiveFns-Bdqm8wjI.cjs");
@@ -3354,6 +3354,20 @@ function cut(base, tool, options) {
3354
3354
  unsafe: true
3355
3355
  });
3356
3356
  }
3357
+ /** Fuse many 3D shapes (boolean union) in a single operation. */
3358
+ function fuseAll(shapes, options) {
3359
+ return require_solidBuilders.fuseAll(shapes.map((s) => resolve(s)), {
3360
+ ...options,
3361
+ unsafe: true
3362
+ });
3363
+ }
3364
+ /** Cut many tool shapes from a base shape (boolean subtraction) in a single operation. */
3365
+ function cutAll(base, tools, options) {
3366
+ return require_solidBuilders.cutAll(resolve(base), tools.map((s) => resolve(s)), {
3367
+ ...options,
3368
+ unsafe: true
3369
+ });
3370
+ }
3357
3371
  /** Compute the intersection of two shapes (boolean common). */
3358
3372
  function intersect(a, b, options) {
3359
3373
  return require_solidBuilders.intersect(resolve(a), resolve(b), {
@@ -6594,7 +6608,7 @@ exports.curveStartPoint = require_curveFns.curveStartPoint;
6594
6608
  exports.curveTangentAt = require_curveFns.curveTangentAt;
6595
6609
  exports.cut = cut;
6596
6610
  exports.cut2D = require_boolean2D.cut2D;
6597
- exports.cutAll = require_solidBuilders.cutAll;
6611
+ exports.cutAll = cutAll;
6598
6612
  exports.cutAllBisect = require_healingFns.cutAllBisect;
6599
6613
  exports.cutBlueprints = require_boolean2D.cutBlueprints;
6600
6614
  exports.cutWithEvolution = require_healingFns.cutWithEvolution;
@@ -6682,7 +6696,7 @@ exports.fromKernelVec = fromKernelVec;
6682
6696
  exports.fromNullable = require_errors.fromNullable;
6683
6697
  exports.fuse = fuse;
6684
6698
  exports.fuse2D = require_boolean2D.fuse2D;
6685
- exports.fuseAll = require_solidBuilders.fuseAll;
6699
+ exports.fuseAll = fuseAll;
6686
6700
  exports.fuseAllBisect = require_healingFns.fuseAllBisect;
6687
6701
  exports.fuseBlueprints = require_boolean2D.fuseBlueprints;
6688
6702
  exports.fuseWithEvolution = require_healingFns.fuseWithEvolution;
package/dist/brepjs.js CHANGED
@@ -13,21 +13,21 @@ import { a as curveIsPeriodic, c as curvePointAt, d as flipOrientation, f as get
13
13
  import { a as meshEdges$1, c as createMeshCache, i as mesh$1, n as exportSTEP, o as meshMultiLOD, r as exportSTL, s as clearMeshCache, t as exportIGES } from "./meshFns-CtchqgDR.js";
14
14
  import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayAccess-DrUGPADn.js";
15
15
  import { _ as makeThreePointArc, d as makeCircle, h as makeLine, l as makeBSplineInterpolation, n as fill, r as makeFace, s as assembleWire } from "./surfaceBuilders-BQx3TjMH.js";
16
- import { _ as section$1, b as split$1, d as booleanPipeline, f as cut$2, g as intersect$2, h as fuseAll, m as fuse$2, p as cutAll, r as makeCylinder, v as sectionToFace$1, y as slice$1 } from "./solidBuilders-Bb5_uC7F.js";
16
+ import { _ as section$1, b as split$1, d as booleanPipeline, f as cut$2, g as intersect$2, h as fuseAll$2, m as fuse$2, p as cutAll$2, r as makeCylinder, v as sectionToFace$1, y as slice$1 } from "./solidBuilders-Bb5_uC7F.js";
17
17
  import { A as edgesOfFace, C as shellWithEvolution, D as getNurbsCurveData, E as fuseAllBisect, F as chamferDistAngle, I as toBufferGeometryData, L as toGroupedBufferGeometryData, M as sharedEdges, N as verticesOfEdge, O as getNurbsSurfaceData, P as wiresOfFace, R as toLODGeometryData, S as intersectWithEvolution, T as cutAllBisect, _ as positionOnCurve, a as healFace, b as filletWithEvolution, c as isValid$1, d as draft$1, f as fillet$1, g as variableFillet, h as thicken$1, i as heal$1, j as facesOfEdge, k as adjacentFaces, l as solidFromShell, m as shell$1, n as fixSelfIntersection, o as healSolid, p as offset$1, r as fixShape, s as healWire, t as autoHeal, u as chamfer$1, v as chamferWithEvolution, w as checkBoolean, x as fuseWithEvolution, y as cutWithEvolution, z as toLineGeometryData } from "./healingFns-Cxi24K_n.js";
18
18
  import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, F as quatRotate, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, N as quatFromAxisAngle, O as prismaticJoint, P as quatFromTo, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, a as deserializeHistory, b as inverseKinematics, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as setJointValues, k as revoluteJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, p as serializeHistory, q as exportAssemblySTEP, r as createHistory, s as getShape, t as thread, u as registerShape, v as importURDF, w as forwardKinematics, x as jointTrajectory, y as jointsFromDH, z as createAssemblyNode } from "./threadFns-BWAL8cAZ.js";
19
19
  import { n as BaseSketcher2d, r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-CxFmYBvh.js";
20
20
  import { a as createTypedFinder, i as wireFinder, n as edgeFinder, r as faceFinder, t as getSingleFace } from "./helpers-CKjmUSf0.js";
21
- import { A as sketchEllipse, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-DiiBS6r0.js";
21
+ import { A as sketchEllipse, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-C63kOWvC.js";
22
22
  import { a as measureDistance, c as measureLinearProps, d as measureVolumeProps, i as measureCurvatureAtMid, l as measureSurfaceProps, n as measureArea, o as measureDistanceProps, r as measureCurvatureAt, s as measureLength, t as createDistanceQuery, u as measureVolume } from "./measureFns-D0e8tGSP.js";
23
23
  import { t as cornerFinder } from "./cornerFinder-7M1wdtcY.js";
24
24
  import { a as fuseBlueprints, c as roundedRectangleBlueprint, i as cutBlueprints, n as fuse2D, o as intersectBlueprints, r as intersect2D, s as polysidesBlueprint, t as cut2D } from "./boolean2D-CIohnI9l.js";
25
25
  import { S as reverseCurve, _ as curve2dIsOnCurve, a as isInside2D, b as curve2dSplitAt, c as scale2D, d as stretch2D, f as toSVGPathD, g as curve2dFirstPoint, h as curve2dDistanceFrom, i as getOrientation2D, l as sketchOnFace2D, m as curve2dBoundingBox, n as createCompoundBlueprint, o as mirror2D, p as translate2D, r as getBounds2D, s as rotate2D, t as createBlueprint, u as sketchOnPlane2D, v as curve2dLastPoint, x as curve2dTangentAt, y as curve2dParameter } from "./blueprintFns-DCA97w4C.js";
26
26
  import { a as importSVG, c as blueprintToDXF, d as exportGltf, f as exportOBJ, i as exportSTEPConfigured, l as exportDXF, n as importSTEP, o as importSVGPathD, r as importSTL, s as exportThreeMF, t as importIGES, u as exportGlb } from "./importFns-CDaNqu4H.js";
27
27
  import { a as revolve$1, c as multiSectionSweep, d as twistExtrude, i as extrudeAll, l as supportExtrude, n as loftAll, o as complexExtrude, r as extrude$1, s as guidedSweep, t as loft$1, u as sweep$1 } from "./loftFns-C8Ep9McS.js";
28
- import { a as Sketch, c as compoundSketchLoft, d as sketchFace, f as sketchLoft, h as sketchWires, i as Sketches, l as compoundSketchRevolve, m as sketchSweep, n as getFont, o as compoundSketchExtrude, p as sketchRevolve, r as loadFont, s as compoundSketchFace, t as textBlueprints, u as sketchExtrude, v as CompoundSketch } from "./textBlueprints-BK3idmq5.js";
28
+ import { a as Sketch, c as compoundSketchLoft, d as sketchFace, f as sketchLoft, h as sketchWires, i as Sketches, l as compoundSketchRevolve, m as sketchSweep, n as getFont, o as compoundSketchExtrude, p as sketchRevolve, r as loadFont, s as compoundSketchFace, t as textBlueprints, u as sketchExtrude, v as CompoundSketch } from "./textBlueprints-CU7MHMR8.js";
29
29
  import { a as makeProjectedEdges, i as projectEdges, n as cameraLookAt, r as createCamera, s as isProjectionPlane, t as cameraFromPlane } from "./cameraFns-BA7CVtWJ.js";
30
- import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-DSYHxQ3W.js";
30
+ import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-BDTWvvyd.js";
31
31
  import { a as updateRoles, i as resolveRef, n as captureHint, o as defaultScorer, r as createRef, t as assignRoles } from "./shapeRefFns-C4JSc_jF.js";
32
32
  import { a as createTaskQueue, c as isEmpty$1, d as isDisposeRequest, f as isErrorResponse, h as isSuccessResponse, i as createWorkerClient, l as pendingCount, m as isOperationRequest, n as createWorkerHandler, o as dequeueTask, p as isInitRequest, r as registerHandler, s as enqueueTask, t as createOperationRegistry, u as rejectAll } from "./workerHandler-CFetYgIm.js";
33
33
  import { C as threePointArc, D as wireLoop, E as wire, S as tangentArc, T as vertex, _ as polygon, a as circle, b as sphere$1, c as cylinder, d as ellipsoid, f as face, g as offsetFace, h as line, i as bsplineApprox, l as ellipse, m as helix, n as bezier, o as compound, p as filledFace, r as box, s as cone, t as addHoles, u as ellipseArc, v as sewShells, w as torus$1, x as subFace, y as solid } from "./primitiveFns-D7R24hoz.js";
@@ -3365,6 +3365,20 @@ function cut(base, tool, options) {
3365
3365
  unsafe: true
3366
3366
  });
3367
3367
  }
3368
+ /** Fuse many 3D shapes (boolean union) in a single operation. */
3369
+ function fuseAll(shapes, options) {
3370
+ return fuseAll$2(shapes.map((s) => resolve(s)), {
3371
+ ...options,
3372
+ unsafe: true
3373
+ });
3374
+ }
3375
+ /** Cut many tool shapes from a base shape (boolean subtraction) in a single operation. */
3376
+ function cutAll(base, tools, options) {
3377
+ return cutAll$2(resolve(base), tools.map((s) => resolve(s)), {
3378
+ ...options,
3379
+ unsafe: true
3380
+ });
3381
+ }
3368
3382
  /** Compute the intersection of two shapes (boolean common). */
3369
3383
  function intersect(a, b, options) {
3370
3384
  return intersect$2(resolve(a), resolve(b), {
@@ -3755,7 +3769,7 @@ function rectangularPattern(shape, options) {
3755
3769
  ];
3756
3770
  copies.push(translate$2(s, offset));
3757
3771
  }
3758
- return fuseAll(copies, { unsafe: true });
3772
+ return fuseAll$2(copies, { unsafe: true });
3759
3773
  }
3760
3774
  //#endregion
3761
3775
  //#region src/topology/wrapperFns.ts
@@ -3872,11 +3886,11 @@ function create3DBooleans(val) {
3872
3886
  ...opts,
3873
3887
  unsafe: true
3874
3888
  }))),
3875
- fuseAll: (tools, opts) => wrap3D(trustAsT(unwrapOrThrow(fuseAll([val, ...tools.map(resolve)], {
3889
+ fuseAll: (tools, opts) => wrap3D(trustAsT(unwrapOrThrow(fuseAll$2([val, ...tools.map(resolve)], {
3876
3890
  ...opts,
3877
3891
  unsafe: true
3878
3892
  })))),
3879
- cutAll: (tools, opts) => wrap3D(trustAsT(unwrapOrThrow(cutAll(val, tools, {
3893
+ cutAll: (tools, opts) => wrap3D(trustAsT(unwrapOrThrow(cutAll$2(val, tools, {
3880
3894
  ...opts,
3881
3895
  unsafe: true
3882
3896
  })))),
@@ -4664,10 +4678,10 @@ var booleans_exports = /* @__PURE__ */ __exportAll({
4664
4678
  checkBoolean: () => checkBoolean,
4665
4679
  convexHull: () => convexHull,
4666
4680
  cut: () => cut,
4667
- cutAll: () => cutAll,
4681
+ cutAll: () => cutAll$2,
4668
4682
  cutAllBisect: () => cutAllBisect,
4669
4683
  fuse: () => fuse,
4670
- fuseAll: () => fuseAll,
4684
+ fuseAll: () => fuseAll$2,
4671
4685
  fuseAllBisect: () => fuseAllBisect,
4672
4686
  hull: () => hull,
4673
4687
  intersect: () => intersect,
@@ -5487,7 +5501,7 @@ function evalFuseAll(node, ctx) {
5487
5501
  if (non.length === 1 && non[0]) return ctx.evalNode(non[0]);
5488
5502
  const resolved = resolveAll(ctx, non, "FuseAll.operand");
5489
5503
  if (!resolved.ok) return resolved;
5490
- return fuseAll(resolved.value, boolOptions(node, ctx));
5504
+ return fuseAll$2(resolved.value, boolOptions(node, ctx));
5491
5505
  }
5492
5506
  function evalCutAll(node, ctx) {
5493
5507
  if (node.base.kind === "Empty") return emptyResult$1("CutAll");
@@ -5497,7 +5511,7 @@ function evalCutAll(node, ctx) {
5497
5511
  if (!base.ok) return base;
5498
5512
  const resolved = resolveAll(ctx, tools, "CutAll.tool");
5499
5513
  if (!resolved.ok) return resolved;
5500
- return cutAll(base.value, resolved.value, boolOptions(node, ctx));
5514
+ return cutAll$2(base.value, resolved.value, boolOptions(node, ctx));
5501
5515
  }
5502
5516
  //#endregion
5503
5517
  //#region src/csg/evaluators/transforms.ts
@@ -15,7 +15,7 @@ import { _ as samePoint$1, b as subtract2d, c as PRECISION_OFFSET, l as add2d, y
15
15
  import { _ as Curve2D, a as edgeToCurve, c as make2dCircle, d as make2dInerpolatedBSplineCurve, f as make2dSegmentCurve, g as approximateAsSvgCompatibleCurve, i as curvesAsEdgesOnPlane, l as make2dEllipse, o as make2dArcFromCenter, t as Blueprint, v as deserializeCurve2D, y as BoundingBox2d } from "./blueprint-BL2TdtBX.js";
16
16
  import { t as cornerFinder } from "./cornerFinder-7M1wdtcY.js";
17
17
  import { c as roundedRectangleBlueprint, n as fuse2D, r as intersect2D, s as polysidesBlueprint, t as cut2D } from "./boolean2D-CIohnI9l.js";
18
- import { _ as wrapSketchDataArray, a as Sketch, g as wrapSketchData, i as Sketches, t as textBlueprints } from "./textBlueprints-BK3idmq5.js";
18
+ import { _ as wrapSketchDataArray, a as Sketch, g as wrapSketchData, i as Sketches, t as textBlueprints } from "./textBlueprints-CU7MHMR8.js";
19
19
  import { i as projectEdges, t as cameraFromPlane } from "./cameraFns-BA7CVtWJ.js";
20
20
  //#region src/2d/lib/stitching.ts
21
21
  /**
@@ -1,4 +1,4 @@
1
- const require_textBlueprints = require("./textBlueprints-BhxAXpVE.cjs");
1
+ const require_textBlueprints = require("./textBlueprints-CKFmYJHD.cjs");
2
2
  const require_shapeTypes = require("./shapeTypes-DwNTiXjG.cjs");
3
3
  const require_vec3 = require("./vec3-CFwOI0ZI.cjs");
4
4
  const require_errors = require("./errors-CXJtc4I7.cjs");
package/dist/index.d.ts CHANGED
@@ -95,7 +95,7 @@ export { getSurfaceType, faceGeomType, faceOrientation, flipFaceOrientation, uvB
95
95
  export { exportSTEP, exportSTL, exportIGES, meshMultiLOD, type ShapeMesh, type EdgeMesh, type MeshOptions, type MultiLODMesh, } from './topology/meshFns.js';
96
96
  export { clearMeshCache, createMeshCache, type MeshCacheContext } from './topology/meshCache.js';
97
97
  export { toBufferGeometryData, toLineGeometryData, toGroupedBufferGeometryData, type BufferGeometryData, type LineGeometryData, type GroupedBufferGeometryData, type BufferGeometryGroup, toLODGeometryData, type LODGeometryData, } from './topology/threeHelpers.js';
98
- export { fuseAll, cutAll, booleanPipeline, type BooleanOptions, type BooleanPipelineStep, } from './topology/booleanFns.js';
98
+ export { booleanPipeline, type BooleanOptions, type BooleanPipelineStep, } from './topology/booleanFns.js';
99
99
  export { fuseAllBisect, cutAllBisect, type BatchBisectResult, type BatchBisectTelemetry, } from './topology/booleanBatchFns.js';
100
100
  export { type PipelineOp } from './topology/booleanFns.js';
101
101
  export { checkBoolean } from './topology/booleanDiagnosticFns.js';
@@ -141,7 +141,7 @@ export type { Shapeable, WrappedMarker, FinderFn, FilletRadius, ChamferDistance,
141
141
  export { resolve, resolve3D } from './topology/apiTypes.js';
142
142
  export { box, cylinder, sphere, cone, torus, ellipsoid, line, circle, ellipse, helix, threePointArc, ellipseArc, bsplineApprox, bezier, tangentArc, wire, wireLoop, face, filledFace, subFace, polygon, vertex, compound, solid, offsetFace, sewShells, addHoles, type BoxOptions, type CylinderOptions, type SphereOptions, type ConeOptions, type TorusOptions, type EllipsoidOptions, type CircleOptions, type EllipseOptions, type HelixOptions, type EllipseArcOptions, } from './topology/primitiveFns.js';
143
143
  export { fill } from './topology/surfaceBuilders.js';
144
- export { translate, rotate, mirror, scale, clone, applyMatrix, composeTransforms, transformCopy, type TransformOp, type ComposedTransform, fuse, cut, intersect, section, sectionToFace, split, slice, fillet, chamfer, shell, offset, thicken, draft, heal, simplify, mesh, meshEdges, describe, toBREP, fromBREP, isValid, isEmpty, type RotateOptions, type MirrorOptions, type ScaleOptions, } from './topology/api.js';
144
+ export { translate, rotate, mirror, scale, clone, applyMatrix, composeTransforms, transformCopy, type TransformOp, type ComposedTransform, fuse, cut, fuseAll, cutAll, intersect, section, sectionToFace, split, slice, fillet, chamfer, shell, offset, thicken, draft, heal, simplify, mesh, meshEdges, describe, toBREP, fromBREP, isValid, isEmpty, type RotateOptions, type MirrorOptions, type ScaleOptions, } from './topology/api.js';
145
145
  export { extrude, revolve, loft, type RevolveOptions, type LoftOptions as CleanLoftOptions, type SweepOptions as CleanSweepOptions, } from './operations/api.js';
146
146
  export { loftAll, type LoftAllEntry } from './operations/loftFns.js';
147
147
  export { thread, type ThreadOptions } from './operations/threadFns.js';
@@ -49,6 +49,12 @@ export default class CompoundSketch implements SketchInterface {
49
49
  revolve(revolutionAxis?: PointInput, config?: {
50
50
  origin?: PointInput;
51
51
  }): Shape3D;
52
- /** Loft between this compound sketch and another with matching sub-sketch counts. */
53
- loftWith(otherCompound: this, loftConfig: LoftOptions): Shape3D;
52
+ /**
53
+ * Loft between this compound sketch and another with matching sub-sketch
54
+ * counts. The target must itself be a compound sketch — lofting a
55
+ * face-with-holes profile to a single-wire one has no defined meaning.
56
+ */
57
+ loftWith(otherCompound: SketchInterface | SketchInterface[], loftConfig?: LoftOptions): Shape3D;
58
+ /** Sweeping a face-with-holes profile has no single well-defined spine. */
59
+ sweepSketch(): Shape3D;
54
60
  }
@@ -41,7 +41,12 @@ export interface SketchInterface {
41
41
  *
42
42
  * Note that all sketches will be deleted by this operation
43
43
  */
44
- loftWith(otherSketches: this | this[], loftConfig: LoftOptions, returnShell?: boolean): Shape3D;
44
+ loftWith(otherSketches: SketchInterface | SketchInterface[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
45
+ /**
46
+ * Sweep a profile sketch (built by the `sketchOnPlane` callback) along this
47
+ * sketch's wire path.
48
+ */
49
+ sweepSketch(sketchOnPlane: (plane: Plane, origin: Vec3) => SketchInterface, sweepConfig?: SweepOptions): Shape3D;
45
50
  }
46
51
  /**
47
52
  * Represent a closed or open wire profile with a default extrusion origin and direction.
@@ -137,7 +142,7 @@ export default class Sketch implements SketchInterface {
137
142
  * @remarks Consumes both this sketch and the one returned by `sketchOnPlane` —
138
143
  * calling either consumer twice throws on the second call.
139
144
  */
140
- sweepSketch(sketchOnPlane: (plane: Plane, origin: Vec3) => this, sweepConfig?: SweepOptions): Shape3D;
145
+ sweepSketch(sketchOnPlane: (plane: Plane, origin: Vec3) => SketchInterface, sweepConfig?: SweepOptions): Shape3D;
141
146
  /** Loft between this sketch and another sketch (or an array of them)
142
147
  *
143
148
  * You can also define a `startPoint` for the loft (that will be placed
@@ -147,5 +152,5 @@ export default class Sketch implements SketchInterface {
147
152
  *
148
153
  * Note that all sketches will be deleted by this operation
149
154
  */
150
- loftWith(otherSketches: this | this[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
155
+ loftWith(otherSketches: SketchInterface | SketchInterface[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
151
156
  }
@@ -5,12 +5,20 @@ import { LoftOptions } from '../operations/loftFns.js';
5
5
  import { OrientedFace, Shape3D, Wire } from '../core/shapeTypes.js';
6
6
  import { PlanarFace } from '../core/validityTypes.js';
7
7
  import { SketchData } from '../2d/blueprints/lib.js';
8
- import { default as Sketch } from './sketch.js';
8
+ import { default as Sketch, SketchInterface } from './sketch.js';
9
9
  import { default as CompoundSketch } from './compoundSketch.js';
10
10
  /** Wrap SketchData into a Sketch instance. */
11
11
  export declare function wrapSketchData(data: SketchData): Sketch;
12
12
  /** Wrap an array of SketchData into a CompoundSketch. */
13
13
  export declare function wrapSketchDataArray(dataArr: SketchData[]): CompoundSketch;
14
+ /**
15
+ * Collapse a sketch-like (the `SketchInterface | Sketches` union that
16
+ * `Drawing.sketchOnPlane` can return) to a single {@link Sketch} for use as a
17
+ * loft or sweep section. A single-wire profile passes through; a multi-piece
18
+ * profile keeps its first wire and disposes the rest, since loft/sweep sections
19
+ * are single wires.
20
+ */
21
+ export declare function asSketch(sketchLike: SketchInterface): Sketch;
14
22
  /** Build a face from a sketch's closed planar wire. */
15
23
  export declare function sketchFace(sketch: Sketch): OrientedFace & PlanarFace;
16
24
  /** Return an independent clone of the sketch's wire. */
@@ -43,13 +51,13 @@ export declare function sketchExtrude(sketch: Sketch, extrusionDistance: number,
43
51
  * @remarks Consumes both this sketch and the one returned by `sketchOnPlane` —
44
52
  * calling either twice throws on the second call.
45
53
  */
46
- export declare function sketchSweep(sketch: Sketch, sketchOnPlane: (plane: Plane, origin: Vec3) => Sketch, sweepConfig?: SweepOptions): Shape3D;
54
+ export declare function sketchSweep(sketch: Sketch, sketchOnPlane: (plane: Plane, origin: Vec3) => SketchInterface, sweepConfig?: SweepOptions): Shape3D;
47
55
  /**
48
56
  * Loft between this sketch and one or more other sketches.
49
57
  *
50
58
  * @remarks Consumes all input sketches — calling twice throws on the second call.
51
59
  */
52
- export declare function sketchLoft(sketch: Sketch, otherSketches: Sketch | Sketch[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
60
+ export declare function sketchLoft(sketch: Sketch, otherSketches: SketchInterface | SketchInterface[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
53
61
  /** Build a face from a compound sketch (outer boundary with holes). */
54
62
  export declare function compoundSketchFace(sketch: CompoundSketch): OrientedFace & PlanarFace;
55
63
  /** Return all wires (outer + holes) combined into a compound shape. */
@@ -71,4 +79,4 @@ export declare function compoundSketchRevolve(sketch: CompoundSketch, revolution
71
79
  origin?: PointInput;
72
80
  }): Shape3D;
73
81
  /** Loft between two compound sketches with matching sub-sketch counts. */
74
- export declare function compoundSketchLoft(sketch: CompoundSketch, other: CompoundSketch, loftConfig: LoftOptions): Shape3D;
82
+ export declare function compoundSketchLoft(sketch: CompoundSketch, other: CompoundSketch, loftConfig?: LoftOptions): Shape3D;
@@ -1,19 +1,38 @@
1
- import { PointInput } from '../core/types.js';
2
- import { ExtrusionProfile } from '../operations/extrudeUtils.js';
3
- import { Compound } from '../core/shapeTypes.js';
1
+ import { PointInput, Vec3 } from '../core/types.js';
2
+ import { ExtrusionProfile, SweepOptions } from '../operations/extrudeUtils.js';
3
+ import { LoftOptions } from '../operations/loftFns.js';
4
+ import { Plane } from '../core/planeTypes.js';
5
+ import { Compound, Face, Shape3D } from '../core/shapeTypes.js';
4
6
  import { default as CompoundSketch } from './compoundSketch.js';
5
- import { default as Sketch } from './sketch.js';
7
+ import { default as Sketch, SketchInterface } from './sketch.js';
6
8
  /**
7
9
  * Batch wrapper around multiple {@link Sketch} or {@link CompoundSketch} instances.
8
10
  *
9
11
  * Applies the same operation (extrude, revolve, etc.) to every contained sketch
10
12
  * and returns the results combined into a single compound shape.
11
13
  *
14
+ * Implements {@link SketchInterface} so it is interchangeable with a single
15
+ * {@link Sketch} in the chained `Drawing.sketchOnPlane(...).extrude()` style.
16
+ * Operations with no per-profile batch meaning (`face`, `loftWith`,
17
+ * `sweepSketch`) require a single contained profile and otherwise throw.
18
+ *
12
19
  * @category Sketching
13
20
  */
14
- export default class Sketches {
21
+ export default class Sketches implements SketchInterface {
15
22
  sketches: Array<Sketch | CompoundSketch>;
16
23
  constructor(sketches: Array<Sketch | CompoundSketch>);
24
+ /**
25
+ * The sole contained {@link Sketch}, for operations that have no
26
+ * multi-profile meaning. Throws when there is more than one profile, or when
27
+ * the single profile is a compound (face-with-holes) sketch.
28
+ */
29
+ private soleSketch;
30
+ /** Build a face from the sole contained profile (see {@link Sketches.faces}). */
31
+ face(): Face;
32
+ /** Loft from the sole contained profile to one or more other sketches. */
33
+ loftWith(otherSketches: SketchInterface | SketchInterface[], loftConfig?: LoftOptions, returnShell?: boolean): Shape3D;
34
+ /** Sweep a profile along the sole contained sketch's wire. */
35
+ sweepSketch(sketchOnPlane: (plane: Plane, origin: Vec3) => SketchInterface, sweepConfig?: SweepOptions): Shape3D;
17
36
  /** Return all wires combined into a single compound shape. */
18
37
  wires(): Compound;
19
38
  /** Return all sketch faces combined into a single compound shape. */
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_textBlueprints = require("./textBlueprints-BhxAXpVE.cjs");
2
+ const require_textBlueprints = require("./textBlueprints-CKFmYJHD.cjs");
3
3
  const require_blueprintSketcher = require("./blueprintSketcher-ChpPo7tq.cjs");
4
- const require_drawFns = require("./drawFns-DWHuHfHQ.cjs");
4
+ const require_drawFns = require("./drawFns-C766Umy1.cjs");
5
5
  //#region src/sketching.ts
6
6
  /**
7
7
  * brepjs/sketching — Sketcher, Drawing, and sketch-to-shape operations.
package/dist/sketching.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as BaseSketcher2d, t as BlueprintSketcher } from "./blueprintSketcher-CxFmYBvh.js";
2
- import { A as sketchEllipse, C as DrawingPen, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, T as Drawing, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-DiiBS6r0.js";
3
- import { a as Sketch, c as compoundSketchLoft, d as sketchFace, f as sketchLoft, h as sketchWires, i as Sketches, l as compoundSketchRevolve, m as sketchSweep, o as compoundSketchExtrude, p as sketchRevolve, s as compoundSketchFace, u as sketchExtrude, v as CompoundSketch } from "./textBlueprints-BK3idmq5.js";
2
+ import { A as sketchEllipse, C as DrawingPen, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, T as Drawing, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-C63kOWvC.js";
3
+ import { a as Sketch, c as compoundSketchLoft, d as sketchFace, f as sketchLoft, h as sketchWires, i as Sketches, l as compoundSketchRevolve, m as sketchSweep, o as compoundSketchExtrude, p as sketchRevolve, s as compoundSketchFace, u as sketchExtrude, v as CompoundSketch } from "./textBlueprints-CU7MHMR8.js";
4
4
  //#region src/sketching.ts
5
5
  /**
6
6
  * brepjs/sketching — Sketcher, Drawing, and sketch-to-shape operations.
package/dist/text.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_textBlueprints = require("./textBlueprints-BhxAXpVE.cjs");
3
- const require_textMetrics = require("./textMetrics-SaXWFfLN.cjs");
2
+ const require_textBlueprints = require("./textBlueprints-CKFmYJHD.cjs");
3
+ const require_textMetrics = require("./textMetrics-BYHtOFYW.cjs");
4
4
  exports.fontMetrics = require_textMetrics.fontMetrics;
5
5
  exports.getFont = require_textBlueprints.getFont;
6
6
  exports.loadFont = require_textBlueprints.loadFont;
package/dist/text.js CHANGED
@@ -1,3 +1,3 @@
1
- import { n as getFont, r as loadFont, t as textBlueprints } from "./textBlueprints-BK3idmq5.js";
2
- import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-DSYHxQ3W.js";
1
+ import { n as getFont, r as loadFont, t as textBlueprints } from "./textBlueprints-CU7MHMR8.js";
2
+ import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-BDTWvvyd.js";
3
3
  export { fontMetrics, getFont, loadFont, sketchText, textBlueprints, textMetrics };
@@ -58,7 +58,7 @@ opentype_js = __toESM(opentype_js, 1);
58
58
  * @see {@link Sketch} for single-wire profiles without holes.
59
59
  * @category Sketching
60
60
  */
61
- var CompoundSketch = class {
61
+ var CompoundSketch = class CompoundSketch {
62
62
  sketches;
63
63
  constructor(sketches) {
64
64
  if (sketches.length === 0) require_errors.bug("CompoundSketch", "Cannot create CompoundSketch with an empty array of sketches");
@@ -102,10 +102,19 @@ var CompoundSketch = class {
102
102
  revolve(revolutionAxis, config = {}) {
103
103
  return compoundSketchRevolve(this, revolutionAxis, config);
104
104
  }
105
- /** Loft between this compound sketch and another with matching sub-sketch counts. */
106
- loftWith(otherCompound, loftConfig) {
105
+ /**
106
+ * Loft between this compound sketch and another with matching sub-sketch
107
+ * counts. The target must itself be a compound sketch — lofting a
108
+ * face-with-holes profile to a single-wire one has no defined meaning.
109
+ */
110
+ loftWith(otherCompound, loftConfig = {}) {
111
+ if (Array.isArray(otherCompound) || !(otherCompound instanceof CompoundSketch)) return require_errors.bug("CompoundSketch.loftWith", "A compound (face-with-holes) sketch can only loft to another compound sketch with the same number of sub-sketches.");
107
112
  return compoundSketchLoft(this, otherCompound, loftConfig);
108
113
  }
114
+ /** Sweeping a face-with-holes profile has no single well-defined spine. */
115
+ sweepSketch() {
116
+ return require_errors.bug("CompoundSketch.sweepSketch", "Sweeping a compound (face-with-holes) profile is not supported — sweep its outer Sketch instead.");
117
+ }
109
118
  };
110
119
  //#endregion
111
120
  //#region src/sketching/sketchFns.ts
@@ -122,6 +131,25 @@ function wrapSketchData(data) {
122
131
  function wrapSketchDataArray(dataArr) {
123
132
  return new CompoundSketch(dataArr.map(wrapSketchData));
124
133
  }
134
+ /**
135
+ * Collapse a sketch-like (the `SketchInterface | Sketches` union that
136
+ * `Drawing.sketchOnPlane` can return) to a single {@link Sketch} for use as a
137
+ * loft or sweep section. A single-wire profile passes through; a multi-piece
138
+ * profile keeps its first wire and disposes the rest, since loft/sweep sections
139
+ * are single wires.
140
+ */
141
+ function asSketch(sketchLike) {
142
+ if (sketchLike instanceof Sketch) return sketchLike;
143
+ const pieces = sketchLike.sketches;
144
+ if (Array.isArray(pieces)) {
145
+ const [first, ...rest] = pieces;
146
+ if (first instanceof Sketch) {
147
+ for (const extra of rest) extra.delete();
148
+ return first;
149
+ }
150
+ }
151
+ require_errors.bug("asSketch", "Expected a single-wire profile to loft or sweep.");
152
+ }
125
153
  /** Build a face from a sketch's closed planar wire. */
126
154
  function sketchFace(sketch) {
127
155
  let face;
@@ -185,13 +213,7 @@ function sketchSweep(sketch, sketchOnPlane, sweepConfig = {}) {
185
213
  const normal = require_vecOps.vecNormalize(require_vecOps.vecScale(require_curveFns.curveTangentAt(sketch.wire, 1e-9), -1));
186
214
  const defaultDir = sketch.defaultDirection;
187
215
  const xDir = require_vecOps.vecScale(require_vecOps.vecCross(normal, defaultDir), -1);
188
- const result = sketchOnPlane(require_planeOps.createPlane([...startPoint], [...xDir], [...normal]), [...startPoint]);
189
- let profile;
190
- if ("sketches" in result && Array.isArray(result.sketches)) {
191
- const pieces = result.sketches;
192
- profile = pieces[0];
193
- for (let i = 1; i < pieces.length; i++) pieces[i]?.delete();
194
- } else profile = result;
216
+ const profile = asSketch(sketchOnPlane(require_planeOps.createPlane([...startPoint], [...xDir], [...normal]), [...startPoint]));
195
217
  const config = {
196
218
  forceProfileSpineOthogonality: true,
197
219
  ...sweepConfig
@@ -207,7 +229,7 @@ function sketchSweep(sketch, sketchOnPlane, sweepConfig = {}) {
207
229
  * @remarks Consumes all input sketches — calling twice throws on the second call.
208
230
  */
209
231
  function sketchLoft(sketch, otherSketches, loftConfig = {}, returnShell = false) {
210
- const sketchArray = Array.isArray(otherSketches) ? [sketch, ...otherSketches] : [sketch, otherSketches];
232
+ const sketchArray = [sketch, ...(Array.isArray(otherSketches) ? otherSketches : [otherSketches]).map(asSketch)];
211
233
  const shape = require_errors.unwrap(require_loftFns.loft(sketchArray.map((s) => s.wire), loftConfig, returnShell));
212
234
  sketchArray.forEach((s) => {
213
235
  s.delete();
@@ -268,7 +290,7 @@ function compoundSketchRevolve(sketch, revolutionAxis, { origin } = {}) {
268
290
  return require_errors.unwrap(require_loftFns.revolve(compoundSketchFace(sketch), center, dir));
269
291
  }
270
292
  /** Loft between two compound sketches with matching sub-sketch counts. */
271
- function compoundSketchLoft(sketch, other, loftConfig) {
293
+ function compoundSketchLoft(sketch, other, loftConfig = {}) {
272
294
  if (sketch.sketches.length !== other.sketches.length) require_errors.bug("CompoundSketch.loftWith", "You need to loft with another compound with the same number of sketches");
273
295
  const shells = sketch.sketches.map((base, cIndex) => {
274
296
  const outer = require_arrayAccess.getAtOrThrow(other.sketches, cIndex);
@@ -436,6 +458,11 @@ var Sketch = class Sketch {
436
458
  * Applies the same operation (extrude, revolve, etc.) to every contained sketch
437
459
  * and returns the results combined into a single compound shape.
438
460
  *
461
+ * Implements {@link SketchInterface} so it is interchangeable with a single
462
+ * {@link Sketch} in the chained `Drawing.sketchOnPlane(...).extrude()` style.
463
+ * Operations with no per-profile batch meaning (`face`, `loftWith`,
464
+ * `sweepSketch`) require a single contained profile and otherwise throw.
465
+ *
439
466
  * @category Sketching
440
467
  */
441
468
  var Sketches = class {
@@ -443,6 +470,30 @@ var Sketches = class {
443
470
  constructor(sketches) {
444
471
  this.sketches = sketches;
445
472
  }
473
+ /**
474
+ * The sole contained {@link Sketch}, for operations that have no
475
+ * multi-profile meaning. Throws when there is more than one profile, or when
476
+ * the single profile is a compound (face-with-holes) sketch.
477
+ */
478
+ soleSketch(op) {
479
+ if (this.sketches.length !== 1) require_errors.bug(`Sketches.${op}`, `Multiple profiles — ${op} each sub-sketch individually.`);
480
+ const only = require_arrayAccess.firstOrThrow(this.sketches);
481
+ if (!(only instanceof Sketch)) require_errors.bug(`Sketches.${op}`, `${op} is only supported on single-wire profiles.`);
482
+ return only;
483
+ }
484
+ /** Build a face from the sole contained profile (see {@link Sketches.faces}). */
485
+ face() {
486
+ if (this.sketches.length !== 1) require_errors.bug("Sketches.face", "Multiple profiles — use faces() to combine them.");
487
+ return require_arrayAccess.firstOrThrow(this.sketches).face();
488
+ }
489
+ /** Loft from the sole contained profile to one or more other sketches. */
490
+ loftWith(otherSketches, loftConfig, returnShell) {
491
+ return this.soleSketch("loftWith").loftWith(otherSketches, loftConfig, returnShell);
492
+ }
493
+ /** Sweep a profile along the sole contained sketch's wire. */
494
+ sweepSketch(sketchOnPlane, sweepConfig) {
495
+ return this.soleSketch("sweepSketch").sweepSketch(sketchOnPlane, sweepConfig);
496
+ }
446
497
  /** Return all wires combined into a single compound shape. */
447
498
  wires() {
448
499
  return require_solidBuilders.makeCompound(this.sketches.map((s) => s instanceof Sketch ? s.wire : s.wires));
@@ -26,7 +26,7 @@ import opentype from "opentype.js";
26
26
  * @see {@link Sketch} for single-wire profiles without holes.
27
27
  * @category Sketching
28
28
  */
29
- var CompoundSketch = class {
29
+ var CompoundSketch = class CompoundSketch {
30
30
  sketches;
31
31
  constructor(sketches) {
32
32
  if (sketches.length === 0) bug("CompoundSketch", "Cannot create CompoundSketch with an empty array of sketches");
@@ -70,10 +70,19 @@ var CompoundSketch = class {
70
70
  revolve(revolutionAxis, config = {}) {
71
71
  return compoundSketchRevolve(this, revolutionAxis, config);
72
72
  }
73
- /** Loft between this compound sketch and another with matching sub-sketch counts. */
74
- loftWith(otherCompound, loftConfig) {
73
+ /**
74
+ * Loft between this compound sketch and another with matching sub-sketch
75
+ * counts. The target must itself be a compound sketch — lofting a
76
+ * face-with-holes profile to a single-wire one has no defined meaning.
77
+ */
78
+ loftWith(otherCompound, loftConfig = {}) {
79
+ if (Array.isArray(otherCompound) || !(otherCompound instanceof CompoundSketch)) return bug("CompoundSketch.loftWith", "A compound (face-with-holes) sketch can only loft to another compound sketch with the same number of sub-sketches.");
75
80
  return compoundSketchLoft(this, otherCompound, loftConfig);
76
81
  }
82
+ /** Sweeping a face-with-holes profile has no single well-defined spine. */
83
+ sweepSketch() {
84
+ return bug("CompoundSketch.sweepSketch", "Sweeping a compound (face-with-holes) profile is not supported — sweep its outer Sketch instead.");
85
+ }
77
86
  };
78
87
  //#endregion
79
88
  //#region src/sketching/sketchFns.ts
@@ -90,6 +99,25 @@ function wrapSketchData(data) {
90
99
  function wrapSketchDataArray(dataArr) {
91
100
  return new CompoundSketch(dataArr.map(wrapSketchData));
92
101
  }
102
+ /**
103
+ * Collapse a sketch-like (the `SketchInterface | Sketches` union that
104
+ * `Drawing.sketchOnPlane` can return) to a single {@link Sketch} for use as a
105
+ * loft or sweep section. A single-wire profile passes through; a multi-piece
106
+ * profile keeps its first wire and disposes the rest, since loft/sweep sections
107
+ * are single wires.
108
+ */
109
+ function asSketch(sketchLike) {
110
+ if (sketchLike instanceof Sketch) return sketchLike;
111
+ const pieces = sketchLike.sketches;
112
+ if (Array.isArray(pieces)) {
113
+ const [first, ...rest] = pieces;
114
+ if (first instanceof Sketch) {
115
+ for (const extra of rest) extra.delete();
116
+ return first;
117
+ }
118
+ }
119
+ bug("asSketch", "Expected a single-wire profile to loft or sweep.");
120
+ }
93
121
  /** Build a face from a sketch's closed planar wire. */
94
122
  function sketchFace(sketch) {
95
123
  let face;
@@ -153,13 +181,7 @@ function sketchSweep(sketch, sketchOnPlane, sweepConfig = {}) {
153
181
  const normal = vecNormalize(vecScale(curveTangentAt(sketch.wire, 1e-9), -1));
154
182
  const defaultDir = sketch.defaultDirection;
155
183
  const xDir = vecScale(vecCross(normal, defaultDir), -1);
156
- const result = sketchOnPlane(createPlane([...startPoint], [...xDir], [...normal]), [...startPoint]);
157
- let profile;
158
- if ("sketches" in result && Array.isArray(result.sketches)) {
159
- const pieces = result.sketches;
160
- profile = pieces[0];
161
- for (let i = 1; i < pieces.length; i++) pieces[i]?.delete();
162
- } else profile = result;
184
+ const profile = asSketch(sketchOnPlane(createPlane([...startPoint], [...xDir], [...normal]), [...startPoint]));
163
185
  const config = {
164
186
  forceProfileSpineOthogonality: true,
165
187
  ...sweepConfig
@@ -175,7 +197,7 @@ function sketchSweep(sketch, sketchOnPlane, sweepConfig = {}) {
175
197
  * @remarks Consumes all input sketches — calling twice throws on the second call.
176
198
  */
177
199
  function sketchLoft(sketch, otherSketches, loftConfig = {}, returnShell = false) {
178
- const sketchArray = Array.isArray(otherSketches) ? [sketch, ...otherSketches] : [sketch, otherSketches];
200
+ const sketchArray = [sketch, ...(Array.isArray(otherSketches) ? otherSketches : [otherSketches]).map(asSketch)];
179
201
  const shape = unwrap(loft(sketchArray.map((s) => s.wire), loftConfig, returnShell));
180
202
  sketchArray.forEach((s) => {
181
203
  s.delete();
@@ -236,7 +258,7 @@ function compoundSketchRevolve(sketch, revolutionAxis, { origin } = {}) {
236
258
  return unwrap(revolve(compoundSketchFace(sketch), center, dir));
237
259
  }
238
260
  /** Loft between two compound sketches with matching sub-sketch counts. */
239
- function compoundSketchLoft(sketch, other, loftConfig) {
261
+ function compoundSketchLoft(sketch, other, loftConfig = {}) {
240
262
  if (sketch.sketches.length !== other.sketches.length) bug("CompoundSketch.loftWith", "You need to loft with another compound with the same number of sketches");
241
263
  const shells = sketch.sketches.map((base, cIndex) => {
242
264
  const outer = getAtOrThrow(other.sketches, cIndex);
@@ -404,6 +426,11 @@ var Sketch = class Sketch {
404
426
  * Applies the same operation (extrude, revolve, etc.) to every contained sketch
405
427
  * and returns the results combined into a single compound shape.
406
428
  *
429
+ * Implements {@link SketchInterface} so it is interchangeable with a single
430
+ * {@link Sketch} in the chained `Drawing.sketchOnPlane(...).extrude()` style.
431
+ * Operations with no per-profile batch meaning (`face`, `loftWith`,
432
+ * `sweepSketch`) require a single contained profile and otherwise throw.
433
+ *
407
434
  * @category Sketching
408
435
  */
409
436
  var Sketches = class {
@@ -411,6 +438,30 @@ var Sketches = class {
411
438
  constructor(sketches) {
412
439
  this.sketches = sketches;
413
440
  }
441
+ /**
442
+ * The sole contained {@link Sketch}, for operations that have no
443
+ * multi-profile meaning. Throws when there is more than one profile, or when
444
+ * the single profile is a compound (face-with-holes) sketch.
445
+ */
446
+ soleSketch(op) {
447
+ if (this.sketches.length !== 1) bug(`Sketches.${op}`, `Multiple profiles — ${op} each sub-sketch individually.`);
448
+ const only = firstOrThrow(this.sketches);
449
+ if (!(only instanceof Sketch)) bug(`Sketches.${op}`, `${op} is only supported on single-wire profiles.`);
450
+ return only;
451
+ }
452
+ /** Build a face from the sole contained profile (see {@link Sketches.faces}). */
453
+ face() {
454
+ if (this.sketches.length !== 1) bug("Sketches.face", "Multiple profiles — use faces() to combine them.");
455
+ return firstOrThrow(this.sketches).face();
456
+ }
457
+ /** Loft from the sole contained profile to one or more other sketches. */
458
+ loftWith(otherSketches, loftConfig, returnShell) {
459
+ return this.soleSketch("loftWith").loftWith(otherSketches, loftConfig, returnShell);
460
+ }
461
+ /** Sweep a profile along the sole contained sketch's wire. */
462
+ sweepSketch(sketchOnPlane, sweepConfig) {
463
+ return this.soleSketch("sweepSketch").sweepSketch(sketchOnPlane, sweepConfig);
464
+ }
414
465
  /** Return all wires combined into a single compound shape. */
415
466
  wires() {
416
467
  return makeCompound(this.sketches.map((s) => s instanceof Sketch ? s.wire : s.wires));
@@ -1,5 +1,5 @@
1
1
  import { A as ok, b as err, d as validationError, t as BrepErrorCode } from "./errors-DNWJsfVU.js";
2
- import { g as wrapSketchData, i as Sketches, n as getFont, t as textBlueprints, v as CompoundSketch } from "./textBlueprints-BK3idmq5.js";
2
+ import { g as wrapSketchData, i as Sketches, n as getFont, t as textBlueprints, v as CompoundSketch } from "./textBlueprints-CU7MHMR8.js";
3
3
  //#region src/text/sketchText.ts
4
4
  /**
5
5
  * Render text as 3D sketch outlines on a plane.
@@ -1,4 +1,4 @@
1
- const require_textBlueprints = require("./textBlueprints-BhxAXpVE.cjs");
1
+ const require_textBlueprints = require("./textBlueprints-CKFmYJHD.cjs");
2
2
  const require_errors = require("./errors-CXJtc4I7.cjs");
3
3
  //#region src/text/sketchText.ts
4
4
  /**
@@ -56,6 +56,10 @@ export declare function transformCopy<T extends AnyShape<Dimension>>(shape: Shap
56
56
  export declare function fuse<T extends Shape3D>(a: Shapeable<T>, b: Shapeable<Shape3D>, options?: booleans.BooleanOptions): Result<T>;
57
57
  /** Cut a tool from a base shape (boolean subtraction). */
58
58
  export declare function cut<T extends Shape3D>(base: Shapeable<T>, tool: Shapeable<Shape3D>, options?: booleans.BooleanOptions): Result<T>;
59
+ /** Fuse many 3D shapes (boolean union) in a single operation. */
60
+ export declare function fuseAll<T extends Shape3D>(shapes: Shapeable<T>[], options?: booleans.BooleanOptions): Result<T>;
61
+ /** Cut many tool shapes from a base shape (boolean subtraction) in a single operation. */
62
+ export declare function cutAll<T extends Shape3D>(base: Shapeable<T>, tools: Shapeable<Shape3D>[], options?: booleans.BooleanOptions): Result<T>;
59
63
  /** Compute the intersection of two shapes (boolean common). */
60
64
  export declare function intersect<T extends Shape3D>(a: Shapeable<T>, b: Shapeable<Shape3D>, options?: booleans.BooleanOptions): Result<T>;
61
65
  /** Section (cross-section) a shape with a plane. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.84.1",
3
+ "version": "18.86.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",
@@ -210,6 +210,7 @@
210
210
  ],
211
211
  "scripts": {
212
212
  "build": "vite build && node scripts/build-quick.js",
213
+ "build:site": "npm run build && npm run build --workspace=brepjs-bim && npm run build --workspace=brepjs-sheetmetal && npm run build --workspace=apps/playground && npm run build --workspace=apps/docs && mkdir -p apps/docs/.vitepress/dist/playground && cp -r apps/playground/dist/. apps/docs/.vitepress/dist/playground/",
213
214
  "dev": "vite build --watch",
214
215
  "prepublishOnly": "npm run build",
215
216
  "prepack": "bash scripts/validate-pack.sh",
@@ -243,7 +244,9 @@
243
244
  "docs:preview": "npm -C apps/docs run preview",
244
245
  "docs:extract-tests": "npx tsx scripts/extract-doc-tests.ts",
245
246
  "docs:render-images": "npx tsx scripts/render-doc-images.ts",
246
- "gen:og": "bash scripts/gen-og.sh",
247
+ "docs:gen-hero": "npx tsx scripts/genHeroFrames.ts",
248
+ "docs:gen-images": "npx tsx scripts/genLandingImages.ts",
249
+ "gen:og": "node scripts/gen-og-docs.mjs && bash scripts/gen-og.sh",
247
250
  "test:docs": "npm run docs:extract-tests && vitest run --project occt-wasm tests/docs",
248
251
  "conformance:generate": "npx tsx scripts/generateConformance.ts",
249
252
  "sync:brepkit-types": "npx tsx scripts/sync-brepkit-types.ts",