partforge 0.158.0 → 0.158.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "partforge",
3
- "version": "0.158.0",
3
+ "version": "0.158.1",
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",
@@ -5,7 +5,9 @@ import { resolveDerivedAttributed } from "./derive.js";
5
5
  import { createProbeKernel } from "./geometry/probe.js";
6
6
  import { inView, placeOf } from "./sub-part-views.js";
7
7
  import { byteAwareReplacer } from "./geometry/solid-hash.js";
8
- import { viewSubParts } from "./part-model.js";
8
+ import { viewSubParts, resolveParamsAttributed, recordedParams } from "./part-model.js";
9
+ import { newReadSink, expandReads } from "./read-recorder.js";
10
+ import { probeSubPartPose } from "./pose-probe-core.js";
9
11
 
10
12
  export const RELEVANT_ALL = Symbol("relevant-all");
11
13
 
@@ -77,10 +79,12 @@ export function subPartParamKeys(part, view, params, readsOf, name) {
77
79
  const rec = readsOf?.(name);
78
80
  if (rec) {
79
81
  // The worker records the BUILD's reads; the sub-part's own show/hide gate
80
- // is added here, as the prediction rung does (subPartReadKeys runs enabled()).
82
+ // and its place() reads in this view are added here, as the prediction rung
83
+ // does (subPartReadKeys runs enabled() and place()).
81
84
  const out = new Set(rec);
82
85
  const sp = part.parts[name];
83
86
  if (sp?.enabled) sp.enabled(recorder(params, out));
87
+ for (const k of placeReadKeys(part, view, params, name)) out.add(k);
84
88
  return [...out].sort();
85
89
  }
86
90
  const reads = subPartReadKeys(part, view, params);
@@ -88,16 +92,53 @@ export function subPartParamKeys(part, view, params, readsOf, name) {
88
92
  return keys.sort();
89
93
  }
90
94
 
91
- // Panel relevance from RECORDED reads: the union over on-screen sub-parts (a
92
- // not-yet-built one contributes its prediction — display only, so a wrong guess
93
- // costs one build's dimming), plus the gate params of every in-view sub-part.
95
+ // The params one sub-part's place() reads in THIS view, derive attribution
96
+ // included. A canonical delivery (jobs.js displayWithReads) is stamped with its
97
+ // BUILD reads alone — the pose is applied as a matrix, and the pose fast path
98
+ // needs a build-only stamp — so a param read only in place() (an explode slider
99
+ // feeding views.cutaway) never reaches the recorded rung. The display side adds
100
+ // it back here, per view: a view whose place() never reads the param leaves it
101
+ // irrelevant. Runs place() on the geometry-free canonical token (no build); the
102
+ // reads count whether or not the probe trusts the pose — they were read either
103
+ // way. Never throws; an unreadable place() contributes nothing (a posed delivery
104
+ // already recorded its place reads in the stamp). `resolved` lets a caller
105
+ // walking many sub-parts run derive() once (a resolveParamsAttributed result).
106
+ export function placeReadKeys(part, view, params, name, resolved) {
107
+ const sp = part.parts[name];
108
+ if (!sp || !placeOf(sp)) return [];
109
+ try {
110
+ resolved ??= resolveParamsAttributed(part, params);
111
+ const sink = newReadSink();
112
+ sink.attribution = resolved.attribution;
113
+ const rec = recordedParams(resolved, sink);
114
+ probeSubPartPose(sp, { view, purpose: "display", p: rec.p, d: rec.d }, { scope: "place" });
115
+ return expandReads(sink);
116
+ } catch {
117
+ return [];
118
+ }
119
+ }
120
+
121
+ // Panel relevance from RECORDED reads: the union over on-screen sub-parts, each
122
+ // with its place() reads in this view (a not-yet-built one contributes its
123
+ // prediction — display only, so a wrong guess costs one build's dimming), plus
124
+ // the gate params of every in-view sub-part.
94
125
  export function recordedRelevantKeys(part, view, params, readsOf) {
95
126
  try {
96
127
  const relevant = new Set();
97
128
  let predicted = null;
129
+ let resolved = null;
98
130
  for (const name of viewSubParts(part, view, params)) {
99
131
  const rec = readsOf(name);
100
- if (rec) { for (const k of rec) relevant.add(k); continue; }
132
+ if (rec) {
133
+ for (const k of rec) relevant.add(k);
134
+ // derive() once per call, and only if some sub-part has a pose to read.
135
+ // A throwing derive() costs only the place reads, never the recorded ones.
136
+ if (resolved === null && placeOf(part.parts[name])) {
137
+ try { resolved = resolveParamsAttributed(part, params); } catch { resolved = false; }
138
+ }
139
+ if (resolved) for (const k of placeReadKeys(part, view, params, name, resolved)) relevant.add(k);
140
+ continue;
141
+ }
101
142
  predicted ??= subPartReadKeys(part, view, params);
102
143
  if (predicted === RELEVANT_ALL) return RELEVANT_ALL;
103
144
  for (const k of predicted.get(name) ?? []) relevant.add(k);