partforge 0.76.0 → 0.78.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.
@@ -454,6 +454,184 @@ export function matchViews(
454
454
  opts?: { scaleAware?: boolean },
455
455
  ): { best: ({ view: string } & MatchScores) | null; views: Record<string, number> };
456
456
 
457
+ // --- describe (the semantic mesh oracle) ------------------------------------
458
+
459
+ /** A raw measurement snapped to intent — never destroys the measurement. */
460
+ export interface Snapped {
461
+ raw: number;
462
+ to: number;
463
+ note: string | null;
464
+ }
465
+
466
+ /** One fitted surface patch. `fit` is the raw per-type fit record (fit.js). */
467
+ export interface DescribeSurface {
468
+ id: string;
469
+ type: string;
470
+ area: number;
471
+ triangles: number;
472
+ rms: number;
473
+ maxDev: number;
474
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- shape varies by surface type (plane/cylinder/cone/torus/sphere)
475
+ fit: Record<string, any>;
476
+ }
477
+
478
+ /** One fitted edge between two surfaces. */
479
+ export interface DescribeArc {
480
+ between: [string, string];
481
+ convexity: "convex" | "concave" | "flat";
482
+ kind: string;
483
+ radius: number | null;
484
+ axis: unknown;
485
+ length: number;
486
+ }
487
+
488
+ /**
489
+ * One recognised feature. Shape varies by `type` (a hole carries `diameter`/
490
+ * `axis`, a fillet carries `radius`/`between`, …) — the fields every family
491
+ * shares are pulled out here; the rest is read by `type`. Every field here
492
+ * must be something a rebuilding agent would want (round 4 review) —
493
+ * `surfaces`/`evidence` are; nothing per-triangle belongs here. describe.js
494
+ * strips any such internal-plumbing field (`faceScope`, a prismatic
495
+ * candidate builder's own per-triangle index map) before a feature reaches
496
+ * this shape, so this catch-all is not a substitute for that discipline.
497
+ */
498
+ export interface DescribeFeature {
499
+ id: string;
500
+ key: string;
501
+ type: string;
502
+ /** The marginal xor-volume reduction that admitted this feature, normalised
503
+ * to the source volume — the fraction of the PART'S VOLUME this feature
504
+ * accounts for, not a certainty rating (see `DescribeScore`'s own note): a
505
+ * small-but-certain feature legitimately reports a small share. `null` for
506
+ * a type acceptCandidates never proposes (fillet, chamfer, revolve, shell). */
507
+ volumeShare: number | null;
508
+ /** Snapped values for whichever of diameter/depth/radius/width/thickness this feature carries. */
509
+ snapped: Record<string, Snapped>;
510
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-type facts (axis, profile, evidence, …)
511
+ [key: string]: any;
512
+ }
513
+
514
+ /** A repetition (grid/linear/circular) or a detected mirror plane over the feature list. */
515
+ export interface DescribePattern {
516
+ id: string;
517
+ type: "grid" | "linear" | "circular";
518
+ members: string[];
519
+ axis: number[] | null;
520
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-type spacing/count facts
521
+ [key: string]: any;
522
+ }
523
+
524
+ /** One connected island of mesh area no surface fit claimed. */
525
+ export interface DescribeResidualRegion {
526
+ triangles: number;
527
+ centroid: number[];
528
+ bounds: { min: number[]; max: number[] };
529
+ }
530
+
531
+ /** One proposed rebuild step, in the order acceptCandidates actually admitted it. */
532
+ export interface DescribeSuggestionStep {
533
+ op: string;
534
+ explains: string[];
535
+ pattern: string | null;
536
+ score: number;
537
+ args: Record<string, unknown>;
538
+ }
539
+
540
+ /** A proposed reconstruction — an interpretation, not a measurement (see `disclaimer`). */
541
+ export interface DescribeSuggestion {
542
+ disclaimer: string;
543
+ params: Array<{ name: string; value: number; from: string }>;
544
+ steps: DescribeSuggestionStep[];
545
+ }
546
+
547
+ /** Which of the report's capped arrays actually hit their ceiling (`DESCRIBE_LIMITS`). */
548
+ export interface DescribeTruncated {
549
+ surfaces: boolean;
550
+ edges: boolean;
551
+ features: boolean;
552
+ patterns: boolean;
553
+ residualRegions: boolean;
554
+ suggestionSteps: boolean;
555
+ }
556
+
557
+ export interface DescribeScore {
558
+ /** Surface coverage from segmentation: fraction of the mesh's area fitted to some surface type. */
559
+ explainedArea: number;
560
+ /** Shape coverage from reconstruction: fraction of the part's volume the accepted features rebuild. */
561
+ explainedVolumeFraction: number;
562
+ xorFraction: number;
563
+ xorVolume: number;
564
+ note: string;
565
+ }
566
+
567
+ /** A full `describe()` report — everything measured, meant for archival/`--json`. */
568
+ export interface DescribeReport {
569
+ source: { name: string | null; digest: string | null; triangles: number; watertight: boolean | null; units: "mm" };
570
+ frame: { up: "+Z"; note: string };
571
+ bounds: { min: number[]; max: number[]; size: number[] };
572
+ counts: { surfaces: number; edges: number };
573
+ surfaces: DescribeSurface[];
574
+ edges: DescribeArc[];
575
+ features: DescribeFeature[];
576
+ patterns: DescribePattern[];
577
+ symmetry: unknown[];
578
+ residual: { areaFraction: number; regions: DescribeResidualRegion[] };
579
+ score: DescribeScore;
580
+ suggestion: DescribeSuggestion | null;
581
+ truncated: DescribeTruncated;
582
+ /** Present only when the acceptance loop hit its boolean budget before converging. */
583
+ warning?: "budget-exceeded";
584
+ }
585
+
586
+ /** The model-facing view: capped arrays elided to counts, a coverage banner first when low. */
587
+ export type DescribeCompactReport = Omit<DescribeReport, "surfaces" | "edges"> & {
588
+ /** Present, and serialized FIRST, only when coverage is below `LOW_COVERAGE`. */
589
+ warning?: string;
590
+ };
591
+
592
+ /** A closed-set failure, returned rather than thrown (spec §5's diagnostic triple). */
593
+ export interface DescribeFailure {
594
+ error: "not-manifold" | "too-large" | "empty" | "budget-exceeded" | "unreadable";
595
+ detail: string;
596
+ diagnostic: { cause: string; location: string; correctiveAction: string };
597
+ source: { name: string | null; digest: string | null; [key: string]: unknown };
598
+ }
599
+
600
+ /** The closed set of error codes `describe()` can return. */
601
+ export const DESCRIBE_ERRORS: readonly string[];
602
+
603
+ /** Report array ceilings (`MAX_SURFACES`, `MAX_FEATURES`, …) — a plain-data module, no imports. */
604
+ export const DESCRIBE_LIMITS: {
605
+ MAX_SURFACES: number;
606
+ MAX_EDGES: number;
607
+ MAX_FEATURES: number;
608
+ MAX_PATTERNS: number;
609
+ MAX_RESIDUAL_REGIONS: number;
610
+ MAX_SUGGESTION_STEPS: number;
611
+ };
612
+
613
+ /** Below this fraction (the worse of `explainedArea`/`explainedVolumeFraction`) `compactDescribe` prepends a warning. */
614
+ export const LOW_COVERAGE: number;
615
+
616
+ /** A fresh, caller-owned digest memo — scope one per worker, or per test. */
617
+ export function describeMemo(): Map<string, DescribeReport>;
618
+
619
+ /**
620
+ * Mesh in, semantic feature report out. `solid` is a LIVE kernel `Solid` — the
621
+ * kernel has no public mesh->solid constructor, so a caller reads one back via
622
+ * `kernel.import(name)`. Keyed by `opts.digest` in `opts.memo` (when both are
623
+ * given): the report depends on nothing but the mesh bytes, so an edit to the
624
+ * part that produced the mesh can never invalidate it.
625
+ */
626
+ export function describe(
627
+ kernel: GeometryKernel,
628
+ solid: Solid,
629
+ opts?: { name?: string; digest?: string; budget?: number; memo?: Map<string, DescribeReport> },
630
+ ): DescribeReport | DescribeFailure;
631
+
632
+ /** The full report, reduced to what a model should read: capped arrays elided to counts, low-coverage banner first. */
633
+ export function compactDescribe(full: DescribeReport): DescribeCompactReport;
634
+
457
635
  // --- rendering --------------------------------------------------------------
458
636
 
459
637
  /** The canonical angle names `renderViews` accepts. */