partforge 0.98.0 → 0.99.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.
@@ -523,6 +523,26 @@ magic vectors. Three habits:
523
523
  body.cutAll([a, b, c]) // and k.union([base, f1, f2]) for additive batches
524
524
  ```
525
525
 
526
+ - **Size cut tools so no two of them land on exactly the same surface.** Booleans are
527
+ cheap right up until two operands share a coincident face, at which point OCCT has to
528
+ classify a surface belonging to both and the search degenerates — seconds become
529
+ minutes, with no error and no warning. Manifold's mesh CSG is unaffected, so the trap
530
+ is invisible until a STEP export (the one format pinned to OCCT) or an OCCT-routed
531
+ build. The classic is a threaded cap: a bore sized straight off the thread's root
532
+ diameter puts the bore wall and the thread root on the same cylinder.
533
+
534
+ ```js
535
+ // ✗ the bore wall lands exactly on the thread root
536
+ const boreD = threadRootD;
537
+ cap.cutAll([k.cylinder({ d: boreD, h }), thread]);
538
+ // ✓ a deliberate gap, far below a printable layer
539
+ const boreD = threadRootD - 2 * 0.05;
540
+ ```
541
+
542
+ The same applies to a cut that stops exactly flush with a face — overshoot it instead,
543
+ which is why cut tools throughout this guide carry `+ 0.4` / `- 0.2` slop. See
544
+ [boolean-coincident-faces-hang](ERROR-PATTERNS.md#boolean-coincident-faces-hang).
545
+
526
546
  The bare `rotate(deg, center, axis)` remains available as the low-level primitive for
527
547
  anything `rotateX/Y/Z`/`rotateAbout` can't express, but prefer the vocabulary above.
528
548
 
@@ -3350,6 +3370,11 @@ symptom first** — it maps error text → cause → fix. The invariants, one li
3350
3370
  - **Keep geometry backend-agnostic** (kernel calls only); only STEP requires OCCT
3351
3371
  ([probe-routed-to-occt](ERROR-PATTERNS.md#probe-routed-to-occt),
3352
3372
  [occt-holes-watertight-na](ERROR-PATTERNS.md#occt-holes-watertight-na)).
3373
+ - **Never let two cut tools share an exactly coincident face** — a bore whose radius
3374
+ equals a thread's root radius, a cut ending flush with a face. Give them 0.05-0.1 mm
3375
+ of deliberate clearance, or overshoot the cut. Mesh CSG shrugs; OCCT's boolean
3376
+ degenerates, so the part previews instantly and the STEP export runs for minutes
3377
+ ([boolean-coincident-faces-hang](ERROR-PATTERNS.md#boolean-coincident-faces-hang)).
3353
3378
 
3354
3379
  ---
3355
3380
 
@@ -61,6 +61,12 @@ The framework itself rebuilds each sub-part fresh per job and applies `place` on
61
61
  - **Cause:** OCCT fillet/chamfer cost scales with the number of selected edges, and an `inPlane` rim selector on a many-point extruded profile selects every polygon edge (hundreds for a gear), so one op call costs seconds — and re-runs on every parameter change while that path is active.
62
62
  - **Fix:** Use `extrude`'s `bevel` option instead of `chamfer` — same geometry, stays on the fast Manifold backend. See [AUTHORING-PARTS.md](AUTHORING-PARTS.md) § "Beveling profile rims: extrude's bevel option".
63
63
 
64
+ ## boolean-coincident-faces-hang
65
+
66
+ - **Symptom:** A part previews instantly but a STEP export (or any OCCT-path build) of one sub-part runs for minutes and never finishes, with no error, no warning, and no progress. Cutting each tool on its own is fast; only the combination hangs. Threaded parts are the usual victims.
67
+ - **Cause:** Two cut tools in the same `cutAll` (or a tool and the body) share an *exactly* coincident face — most often a bore whose radius equals a thread's root radius, so the bore wall and the thread root lie on the same cylinder. OCCT's boolean has to classify a surface that is simultaneously on both operands, and the intersection search degenerates. Manifold's mesh CSG does not care, which is why the preview is fine and only the exact kernel suffers. Measured on one real part: bore alone 0.4 s, thread alone 5.4 s, both together did not finish in fifteen minutes; moving the bore 0.05 mm brought the pair to 12.6 s.
68
+ - **Fix:** Give the surfaces a deliberate clearance instead of letting them land on the same number. Derive one from the other with an explicit gap — `const boreD = threadRootD - 2 * boreClearance;` with `boreClearance` around 0.05-0.1 mm — rather than reusing the same expression for both. The gap is far below a printable layer, so the fit is unchanged. The same rule covers a cut that ends exactly flush with a face (overshoot it by a few tenths, as the surrounding examples do with `+ 0.4` / `- 0.2`) and two tools that abut exactly end-to-end.
69
+
64
70
  ## chamfer-rescue-bisection
65
71
 
66
72
  - **Symptom:** `partforge: chamfer` warning saying the distance `over-ran the geometry — reduced to` a smaller one (or `has no valid distance`), with an attempt count and elapsed seconds, alongside slow builds.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "partforge",
3
- "version": "0.98.0",
3
+ "version": "0.99.0",
4
4
  "description": "Turn a declarative part definition into a parametric-CAD web app (three.js + Manifold/Replicad). Requires a Vite-based consumer.",
5
5
  "type": "module",
6
6
  "license": "MIT",