partforge 0.109.1 → 0.111.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/types/oracle.d.ts CHANGED
@@ -12,7 +12,7 @@ export {
12
12
  type MeasureReport, type SubPartFacts, type AggregateFacts, type BuiltSubPart,
13
13
  type VerifyReport, type VerifyCaseResult, type VerifyCheck, type CheckStatus,
14
14
  // mesh facts, gaps, BVH, min wall
15
- meshVolume, bboxSize, bounds, meshArea, assemblyGaps, meshGaps, buildBVH, meshTriangles, minWall,
15
+ meshVolume, bboxSize, bounds, meshArea, assemblyGaps, meshGaps, buildBVH, meshTriangles, minWall, overhang,
16
16
  type Gap, type BVH,
17
17
  // mesh file parsers (the import pipeline's own readers)
18
18
  parseStl, parse3MF,
package/types/part.d.ts CHANGED
@@ -408,6 +408,13 @@ export interface DfmProfile {
408
408
  bed?: [number, number, number];
409
409
  /** Minimum wall in mm — a warning, never a gate. */
410
410
  minWall?: number;
411
+ /**
412
+ * Steepest unsupported face the process prints cleanly, in degrees from
413
+ * vertical (45 on the FDM profiles; absent on resin). Checked only for a part
414
+ * that also declares `verify.orientation: "print"`; `null` switches it off
415
+ * under a named base. A warning, never a gate.
416
+ */
417
+ overhang?: number | null;
411
418
  /** Carried for a future gap check; not enforced yet. */
412
419
  clearance?: number;
413
420
  /** Inherit from a named profile and override the rest. */
@@ -441,6 +448,12 @@ export interface SubPartExpectations {
441
448
  boundsMin?: Expectation;
442
449
  boundsMax?: Expectation;
443
450
  minWall?: Expectation;
451
+ /**
452
+ * Unsupported downward-facing surface in mm² (faces steeper than the profile's
453
+ * `overhang` angle, bed at the sub-part's own lowest Z). Measured only under
454
+ * `verify.orientation: "print"`; a warning, never a gate.
455
+ */
456
+ overhangArea?: Expectation;
444
457
  /** Symmetric-difference volume vs. the sub-part's declared `reference` import. */
445
458
  refXorVolume?: Expectation;
446
459
  /** Percent volume delta vs. the sub-part's declared `reference` import. */
@@ -476,6 +489,13 @@ export interface ExpectMap {
476
489
  export interface VerifyBlock<P = ResolvedParams, D = Derived> {
477
490
  /** A named DFM profile or an inline one. */
478
491
  process?: DfmProfileName | DfmProfile;
492
+ /**
493
+ * `"print"` declares the part is laid out for its bed — Z up, each sub-part's
494
+ * bed at its own lowest Z — which is the only way the profile's `overhang`
495
+ * check is armed. Omit it while a part is still being shaped or is bound for
496
+ * another process.
497
+ */
498
+ orientation?: "print";
479
499
  /** Which cases to check; default is `"defaults"` plus every preset name. */
480
500
  cases?: string[];
481
501
  /**
@@ -250,6 +250,17 @@ export function minWall(
250
250
  totalTriangles: number;
251
251
  } | null;
252
252
 
253
+ /**
254
+ * Unsupported downward-facing surface (oracle/overhang.js): the mm² of faces
255
+ * steeper than `maxAngle` from vertical, excluding the footprint on the bed
256
+ * (`bedZ`, else the mesh's lowest Z) and a near-bed band. One pass, no index;
257
+ * `null` only for an empty mesh. Bridges and bore ceilings count.
258
+ */
259
+ export function overhang(
260
+ mesh: Mesh,
261
+ opts?: { maxAngle?: number; bedZ?: number; bedEps?: number; bedBand?: number },
262
+ ): { area: number; worstAngle: number | null; at: Point3 | null } | null;
263
+
253
264
  // --- measure ----------------------------------------------------------------
254
265
 
255
266
  export interface SubPartFacts {
@@ -272,6 +283,16 @@ export interface SubPartFacts {
272
283
  minWallAt: number[] | null;
273
284
  minWallSampled: boolean;
274
285
  minWallSamples: { sampled: number; total: number } | null;
286
+ /**
287
+ * Unsupported downward-facing area in mm² (oracle/overhang.js), `null` when the
288
+ * part is not laid out for a bed (`verify.orientation: "print"` under a
289
+ * profile with an `overhang` angle). Bridges and bore ceilings count.
290
+ */
291
+ overhangArea: number | null;
292
+ /** Steepest offending face, degrees from vertical; `null` when none. */
293
+ overhangAngle: number | null;
294
+ /** Centroid of the largest offending face; `null` when none. */
295
+ overhangAt: number[] | null;
275
296
  }
276
297
 
277
298
  export interface AggregateFacts {
@@ -289,6 +310,8 @@ export interface MeasureReport {
289
310
  view: string;
290
311
  /** Whether this run cast min-wall rays at all. */
291
312
  measuredMinWall: boolean;
313
+ /** The overhang angle every sub-part's `overhangArea` was measured against, `null` when the pass did not run. */
314
+ measuredOverhang: number | null;
292
315
  subparts: SubPartFacts[];
293
316
  aggregate: AggregateFacts;
294
317
  overlaps: Overlap[];
@@ -308,6 +331,12 @@ export function measure(
308
331
  params?: ResolvedParams,
309
332
  opts?: {
310
333
  minWall?: boolean;
334
+ /**
335
+ * The overhang angle to measure against (degrees from vertical), or `null`
336
+ * for "not checked". Omitted, measure derives it from the part's own
337
+ * `verify` block the way verify does.
338
+ */
339
+ overhang?: number | null;
311
340
  gapThreshold?: number;
312
341
  /**
313
342
  * A build of this view the caller already has, measured instead of building a
@@ -323,7 +352,8 @@ export function measure(
323
352
  export type CheckStatus = "pass" | "fail" | "warn" | "skip";
324
353
 
325
354
  export interface VerifyCheck {
326
- scope: "view" | "subpart";
355
+ /** `"part"` is the vacuous-verify notice; `"case"` the quick-lap "not measured" marker. */
356
+ scope: "view" | "subpart" | "part" | "case";
327
357
  /** The sub-part name, `"a×b"` for a pair check, or `null` for a scalar view metric. */
328
358
  subpart: string | null;
329
359
  metric: string;
@@ -339,7 +369,7 @@ export interface VerifyCheck {
339
369
  hint?: string;
340
370
  /** A stable ERROR-PATTERNS.md entry id. */
341
371
  pattern?: string;
342
- /** A caveat about HOW the value was measured today only `minWall` sets one. */
372
+ /** A measurement caveat or companion reading `minWall` (sampling) and `overhangArea` (the steepest angle) set one. */
343
373
  note?: string;
344
374
  /** `[x, y, z]` in mm, for the metrics that have one. */
345
375
  location?: number[] | null;
@@ -353,13 +383,25 @@ export interface VerifyCaseResult {
353
383
  }
354
384
 
355
385
  export interface VerifyReport {
356
- /** True when no check has status `"fail"`. */
357
- ok: boolean;
386
+ /**
387
+ * Tri-state: `true` when every declared check passed, `false` on any gate
388
+ * failure, `null` when no verdict can be given — a quick lap that could not
389
+ * measure a gate, or a part that declared no expectations at all (`declared`
390
+ * is 0 and `warnings` carries a `no expectations declared` notice). Never read
391
+ * `null` as a pass.
392
+ */
393
+ ok: boolean | null;
358
394
  view: string;
359
395
  cases: VerifyCaseResult[];
360
396
  /** Every failing check, flattened, each tagged with its `case`. */
361
397
  failures: Array<VerifyCheck & { case: string }>;
362
398
  warnings: Array<VerifyCheck & { case: string }>;
399
+ /** Checks a quick lap could not evaluate. */
400
+ unevaluated: Array<VerifyCheck & { case: string }>;
401
+ /** Check instances the part or its profile declared, across cases (near-miss notices excluded). */
402
+ declared: number;
403
+ /** Of `declared`, how many were actually answered — a skipped or unevaluated check is not. Zero withholds `ok`. */
404
+ evaluated: number;
363
405
  }
364
406
 
365
407
  /**