@photivra/engine 2.19.0 → 2.21.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.
Files changed (242) hide show
  1. package/CHANGELOG.md +289 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +7 -5
  4. package/README.md +7 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +21 -10
  7. package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
  8. package/dist/capture/dng-lens-correction-opcodes.js +119 -0
  9. package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
  10. package/dist/capture/exposure-build-up-stage.d.ts +16 -0
  11. package/dist/capture/exposure-build-up-stage.js +4 -0
  12. package/dist/capture/exposure-build-up-stage.js.map +1 -0
  13. package/dist/capture/native-environment-lean.d.ts +2 -1
  14. package/dist/capture/native-environment-lean.js +42 -78
  15. package/dist/capture/native-environment-lean.js.map +1 -1
  16. package/dist/capture/native-environment-tabulated.js +4 -3
  17. package/dist/capture/native-environment-tabulated.js.map +1 -1
  18. package/dist/capture/native-raw-dng.d.ts +6 -0
  19. package/dist/capture/native-raw-dng.js +16 -2
  20. package/dist/capture/native-raw-dng.js.map +1 -1
  21. package/dist/capture/native-raw.d.ts +3 -2
  22. package/dist/capture/native-raw.js +5 -4
  23. package/dist/capture/native-raw.js.map +1 -1
  24. package/dist/capture/sensor-raw-producer.d.ts +4 -1
  25. package/dist/capture/sensor-raw-producer.js +15 -8
  26. package/dist/capture/sensor-raw-producer.js.map +1 -1
  27. package/dist/core/shared-helpers.d.ts +2 -0
  28. package/dist/core/shared-helpers.js +4 -0
  29. package/dist/core/shared-helpers.js.map +1 -1
  30. package/dist/core/version.d.ts +1 -1
  31. package/dist/core/version.js +1 -1
  32. package/dist/core/version.js.map +1 -1
  33. package/dist/equipment/generic-lens-catalogue.d.ts +11 -4
  34. package/dist/equipment/generic-lens-catalogue.js +49 -7
  35. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  36. package/dist/equipment/generic-tier-assets.d.ts +32 -0
  37. package/dist/equipment/generic-tier-assets.js +18 -14
  38. package/dist/equipment/generic-tier-assets.js.map +1 -1
  39. package/dist/equipment/generic-tier-presets.js +2 -3
  40. package/dist/equipment/generic-tier-presets.js.map +1 -1
  41. package/dist/equipment/sensor-profile-body.d.ts +78 -0
  42. package/dist/equipment/sensor-profile-body.js +77 -0
  43. package/dist/equipment/sensor-profile-body.js.map +1 -0
  44. package/dist/equipment/sensor-profiles.d.ts +8 -0
  45. package/dist/equipment/sensor-profiles.js +82 -0
  46. package/dist/equipment/sensor-profiles.js.map +1 -0
  47. package/dist/facade/catalogue-lens-signal.d.ts +2 -2
  48. package/dist/facade/catalogue-lens-signal.js +58 -71
  49. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  50. package/dist/facade/chart-radiance-batch.js +2 -1
  51. package/dist/facade/chart-radiance-batch.js.map +1 -1
  52. package/dist/facade/defocus-image-space-provider.d.ts +7 -0
  53. package/dist/facade/defocus-image-space-provider.js +64 -76
  54. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  55. package/dist/facade/display-view-gpu-session.js +2 -0
  56. package/dist/facade/display-view-gpu-session.js.map +1 -1
  57. package/dist/facade/display-view-gpu.d.ts +13 -5
  58. package/dist/facade/display-view-gpu.js +4 -4
  59. package/dist/facade/display-view-gpu.js.map +1 -1
  60. package/dist/facade/display-view-lens-signal.d.ts +1 -1
  61. package/dist/facade/display-view-lens-signal.js +30 -26
  62. package/dist/facade/display-view-lens-signal.js.map +1 -1
  63. package/dist/facade/display-view.d.ts +3 -1
  64. package/dist/facade/display-view.js +6 -24
  65. package/dist/facade/display-view.js.map +1 -1
  66. package/dist/facade/expected-signal-setup.d.ts +3 -1
  67. package/dist/facade/expected-signal-setup.js +1 -1
  68. package/dist/facade/expected-signal-setup.js.map +1 -1
  69. package/dist/facade/expected-signal.d.ts +47 -14
  70. package/dist/facade/expected-signal.js +117 -112
  71. package/dist/facade/expected-signal.js.map +1 -1
  72. package/dist/facade/exposure-build-up.d.ts +70 -0
  73. package/dist/facade/exposure-build-up.js +164 -0
  74. package/dist/facade/exposure-build-up.js.map +1 -0
  75. package/dist/facade/fast-methods.d.ts +50 -0
  76. package/dist/facade/fast-methods.js +58 -0
  77. package/dist/facade/fast-methods.js.map +1 -0
  78. package/dist/facade/fast-resolution.d.ts +12 -0
  79. package/dist/facade/fast-resolution.js +18 -0
  80. package/dist/facade/fast-resolution.js.map +1 -0
  81. package/dist/facade/generic-lens-capture.d.ts +5 -4
  82. package/dist/facade/generic-lens-capture.js +5 -4
  83. package/dist/facade/generic-lens-capture.js.map +1 -1
  84. package/dist/facade/image-plane-enable.d.ts +7 -0
  85. package/dist/facade/image-plane-enable.js +17 -0
  86. package/dist/facade/image-plane-enable.js.map +1 -0
  87. package/dist/facade/image-plane-lattice-data.d.ts +6 -0
  88. package/dist/facade/image-plane-lattice-data.js +10 -0
  89. package/dist/facade/image-plane-lattice-data.js.map +1 -0
  90. package/dist/facade/image-plane-support.d.ts +33 -0
  91. package/dist/facade/image-plane-support.js +11 -0
  92. package/dist/facade/image-plane-support.js.map +1 -0
  93. package/dist/facade/image-plane.d.ts +36 -0
  94. package/dist/facade/image-plane.js +353 -0
  95. package/dist/facade/image-plane.js.map +1 -0
  96. package/dist/facade/mixed-illumination.d.ts +108 -0
  97. package/dist/facade/mixed-illumination.js +174 -0
  98. package/dist/facade/mixed-illumination.js.map +1 -0
  99. package/dist/facade/representative-light-sources.d.ts +72 -0
  100. package/dist/facade/representative-light-sources.js +124 -0
  101. package/dist/facade/representative-light-sources.js.map +1 -0
  102. package/dist/facade/scene-radiance.d.ts +129 -4
  103. package/dist/facade/scene-radiance.js +48 -13
  104. package/dist/facade/scene-radiance.js.map +1 -1
  105. package/dist/facade/simulate-capture.d.ts +88 -19
  106. package/dist/facade/simulate-capture.js +374 -145
  107. package/dist/facade/simulate-capture.js.map +1 -1
  108. package/dist/facade/simulate-display-view.d.ts +3 -2
  109. package/dist/facade/simulate-display-view.js +36 -18
  110. package/dist/facade/simulate-display-view.js.map +1 -1
  111. package/dist/gpu/chart-composite-gpu.d.ts +13 -1
  112. package/dist/gpu/chart-composite-gpu.js +48 -9
  113. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  114. package/dist/gpu/chart-radiance-gpu.js +4 -30
  115. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  116. package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
  117. package/dist/gpu/display-view-composite-material-module.js +39 -13
  118. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  119. package/dist/gpu/display-view-image-material-module.d.ts +86 -0
  120. package/dist/gpu/display-view-image-material-module.js +339 -0
  121. package/dist/gpu/display-view-image-material-module.js.map +1 -0
  122. package/dist/gpu/display-view-image-quality.d.ts +45 -0
  123. package/dist/gpu/display-view-image-quality.js +380 -0
  124. package/dist/gpu/display-view-image-quality.js.map +1 -0
  125. package/dist/gpu/display-view-quality-gpu.d.ts +67 -1
  126. package/dist/gpu/display-view-quality-gpu.js +136 -205
  127. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  128. package/dist/gpu/display-view-speed-gpu.d.ts +51 -3
  129. package/dist/gpu/display-view-speed-gpu.js +131 -93
  130. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  131. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +29 -15
  132. package/dist/gpu/wgsl-display-view-quality-kernel.js +58 -36
  133. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  134. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +21 -6
  135. package/dist/gpu/wgsl-display-view-speed-kernel.js +46 -11
  136. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  137. package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
  138. package/dist/gpu/wgsl-exact-sum.js +42 -0
  139. package/dist/gpu/wgsl-exact-sum.js.map +1 -0
  140. package/dist/index.d.ts +18 -3
  141. package/dist/index.js +12 -1
  142. package/dist/index.js.map +1 -1
  143. package/dist/motion/subject-motion.d.ts +66 -0
  144. package/dist/motion/subject-motion.js +52 -0
  145. package/dist/motion/subject-motion.js.map +1 -0
  146. package/dist/optics/aperture-diameter.d.ts +47 -0
  147. package/dist/optics/aperture-diameter.js +50 -0
  148. package/dist/optics/aperture-diameter.js.map +1 -0
  149. package/dist/optics/detail-limit.d.ts +182 -0
  150. package/dist/optics/detail-limit.js +336 -0
  151. package/dist/optics/detail-limit.js.map +1 -0
  152. package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
  153. package/dist/optics/generic-lens-lateral-color.js +77 -0
  154. package/dist/optics/generic-lens-lateral-color.js.map +1 -0
  155. package/dist/optics/generic-lens-psf.d.ts +18 -2
  156. package/dist/optics/generic-lens-psf.js +79 -48
  157. package/dist/optics/generic-lens-psf.js.map +1 -1
  158. package/dist/optics/lens-psf-profile.d.ts +7 -0
  159. package/dist/optics/lens-psf-profile.js +2 -1
  160. package/dist/optics/lens-psf-profile.js.map +1 -1
  161. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  162. package/dist/optics/scene-to-sensor-irradiance.js +7 -0
  163. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  164. package/dist/output/crop-to-match-focal-length.d.ts +62 -0
  165. package/dist/output/crop-to-match-focal-length.js +63 -0
  166. package/dist/output/crop-to-match-focal-length.js.map +1 -0
  167. package/dist/output/developed-lens-corrections.d.ts +19 -5
  168. package/dist/output/developed-lens-corrections.js +30 -11
  169. package/dist/output/developed-lens-corrections.js.map +1 -1
  170. package/dist/output/generic-lens-corrections.d.ts +41 -3
  171. package/dist/output/generic-lens-corrections.js +57 -9
  172. package/dist/output/generic-lens-corrections.js.map +1 -1
  173. package/dist/output/lens-corrections.d.ts +12 -2
  174. package/dist/output/lens-corrections.js +22 -7
  175. package/dist/output/lens-corrections.js.map +1 -1
  176. package/dist/output/print-contrast-reference.js +1 -1
  177. package/dist/output/print-contrast-reference.js.map +1 -1
  178. package/dist/output/print-plan.d.ts +41 -5
  179. package/dist/output/print-plan.js +59 -14
  180. package/dist/output/print-plan.js.map +1 -1
  181. package/dist/output/print-size-blur-noise.d.ts +110 -0
  182. package/dist/output/print-size-blur-noise.js +138 -0
  183. package/dist/output/print-size-blur-noise.js.map +1 -0
  184. package/dist/sensor/binomial-sample.d.ts +3 -0
  185. package/dist/sensor/binomial-sample.js +86 -0
  186. package/dist/sensor/binomial-sample.js.map +1 -0
  187. package/dist/sensor/eqe-electron-rate.js +1 -7
  188. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  189. package/dist/sensor/exposure-window.d.ts +2 -0
  190. package/dist/sensor/exposure-window.js +2 -2
  191. package/dist/sensor/exposure-window.js.map +1 -1
  192. package/dist/sensor/readout-timing.js +2 -23
  193. package/dist/sensor/readout-timing.js.map +1 -1
  194. package/dist/sensor/response-application-compatibility.js +1 -6
  195. package/dist/sensor/response-application-compatibility.js.map +1 -1
  196. package/dist/sensor/responsivity-photocurrent.js +1 -7
  197. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  198. package/dist/sensor/sensor-characteristics.d.ts +147 -0
  199. package/dist/sensor/sensor-characteristics.js +126 -0
  200. package/dist/sensor/sensor-characteristics.js.map +1 -0
  201. package/dist/sensor/spatial-sample-reduction.js +1 -5
  202. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  203. package/dist/sensor/spectral-quadrature.js +1 -6
  204. package/dist/sensor/spectral-quadrature.js.map +1 -1
  205. package/dist/sensor/temporal-photo-signal.d.ts +3 -0
  206. package/dist/sensor/temporal-photo-signal.js +5 -2
  207. package/dist/sensor/temporal-photo-signal.js.map +1 -1
  208. package/dist/stabilization/system.d.ts +9 -0
  209. package/dist/stabilization/system.js +5 -6
  210. package/dist/stabilization/system.js.map +1 -1
  211. package/docs/AGENT_GUIDE.md +5 -0
  212. package/docs/API_REFERENCE.md +173 -2
  213. package/docs/QUICK_START.md +213 -6
  214. package/docs/USAGE.md +157 -3
  215. package/docs/api/capture-native-raw-dng.md +6 -0
  216. package/docs/api/core-version.md +1 -1
  217. package/docs/api/equipment-generic-lens-catalogue.md +17 -7
  218. package/docs/api/equipment-sensor-profile-body.md +64 -0
  219. package/docs/api/equipment-sensor-profiles.md +86 -0
  220. package/docs/api/exports.json +357 -2
  221. package/docs/api/facade-display-view-gpu.md +5 -2
  222. package/docs/api/facade-exposure-build-up.md +105 -0
  223. package/docs/api/facade-fast-methods.md +99 -0
  224. package/docs/api/facade-image-plane-enable.md +16 -0
  225. package/docs/api/facade-mixed-illumination.md +67 -0
  226. package/docs/api/facade-representative-light-sources.md +131 -0
  227. package/docs/api/facade-scene-radiance.md +54 -3
  228. package/docs/api/facade-simulate-capture.md +60 -15
  229. package/docs/api/facade-simulate-display-view.md +3 -2
  230. package/docs/api/motion-subject-motion.md +23 -0
  231. package/docs/api/optics-aperture-diameter.md +51 -0
  232. package/docs/api/optics-detail-limit.md +203 -0
  233. package/docs/api/optics-generic-lens-lateral-color.md +79 -0
  234. package/docs/api/output-crop-to-match-focal-length.md +67 -0
  235. package/docs/api/output-developed-lens-corrections.md +10 -1
  236. package/docs/api/output-generic-lens-corrections.md +40 -2
  237. package/docs/api/output-lens-corrections.md +11 -1
  238. package/docs/api/output-print-plan.md +35 -1
  239. package/docs/api/output-print-size-blur-noise.md +115 -0
  240. package/docs/api/sensor-sensor-characteristics.md +105 -0
  241. package/llms.txt +6 -0
  242. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ # facade/exposure-build-up.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## EXPOSURE_BUILD_UP_MAXIMUM_FRACTIONS
6
+
7
+ The most fractions one build-up takes.
8
+
9
+ ```ts
10
+ EXPOSURE_BUILD_UP_MAXIMUM_FRACTIONS = 64
11
+ ```
12
+
13
+ ## EXPOSURE_BUILD_UP_STREAM_BASE
14
+
15
+ The first stream ID of the build-up's per-site random streams (`deriveNativeSiteStreamSeedUint32`): fraction j's photo split,
16
+ dark split and readout use streams base + 3j, + 3j + 1 and + 3j + 2 (j = 0 … 63). The whole exposure keeps the ordinary
17
+ streams 0 (charge) and 1 (read noise).
18
+
19
+ ```ts
20
+ EXPOSURE_BUILD_UP_STREAM_BASE = 0x42550000
21
+ ```
22
+
23
+ ## simulateExposureBuildUp
24
+
25
+ Renders fractions of one exposure as one accumulating process (#534), the way a camera's live readout of a long exposure
26
+ would show the light building up. Each fraction is read out and developed as its own photo.
27
+
28
+ The model: photon and dark-charge arrivals at a site are Poisson processes (photon shot noise: EMVA 1288 Release 4.0,
29
+ section 2, free), with any rate over time. Given the count n over [0, t] of a Poisson process with mean Λ(t), its arrival
30
+ times are n independent times with distribution Λ(s)/Λ(t) on [0, t], so its count over [0, s] is Binomial(n, Λ(s)/Λ(t))
31
+ (R. G. Gallager, Discrete Stochastic Processes, chapter 2, sections 2.4 and 2.5, MIT OpenCourseWare 6.262, free).
32
+
33
+ The method: the whole exposure is `simulateCapture(options)` itself, with its ordinary draws and seeds. Each earlier fraction
34
+ fⱼ is a capture of the same request in manual mode at the whole exposure's resolved f-number and ISO and the shutter fⱼ·t,
35
+ so its expected signal at every site is integrated from the spectrum over the window [0, fⱼ·t] by the capture itself (the
36
+ camera rotation, `motion.subject` and mixed lighting included, and each row's own window with a rolling shutter). Its
37
+ counts are not drawn anew: going down the fractions, each site's photo and dark counts are N(fⱼ) ~ Binomial(N(fⱼ₊₁), λ(fⱼ) /
38
+ λ(fⱼ₊₁)), with λ the site's expected mean in each render (after PRNU and DSNU). So every fraction's counts are Poisson with
39
+ that fraction's own mean (the mean and variance of `simulateCapture` at fⱼ·t), they never decrease from one fraction to the
40
+ next, and two fractions' counts covary by the earlier one's mean. Fixed-pattern noise is the sensor's own and the same in
41
+ every fraction. Each fraction's charge is clipped at the full well, then read once: its own read noise (dual gain as the ISO
42
+ selects), pre-ADC and ADC saturation and quantization at the ADC bit depth.
43
+
44
+ The windows are nested, so the true means never decrease; the capture's temporal midpoint quadrature can still put an
45
+ earlier fraction's estimate above a later one's at a site (motion blur). There the split takes probability 1 (the count is
46
+ kept), and the `exposure.buildUp` assumption counts those sites (`nonMonotoneSites`): an approximation of the quadrature,
47
+ not of the binomial law. Raise `sensor.temporalSampleCount` to remove them.
48
+
49
+ Cost: one capture per fraction, plus about 64 bytes per native site for the chain. Exact or fast: the request's own `fast`
50
+ (and the format default) applies to every fraction; the build-up adds no fast method, so with `fast: false` (the calculator's
51
+ setting) every fraction is an exact capture. On screen only: formats "srgb-8" and "srgb-16" (a fraction's JPEG, PNG or DNG
52
+ is refused). A fraction's realization depends on the whole `fractions` list (each step of the chain has its own streams);
53
+ the same request and list always give the same frames.
54
+
55
+ ```ts
56
+ export async function simulateExposureBuildUp(options: SimulateCaptureOptions, buildUp: SimulateExposureBuildUpOptions): Promise<SimulateExposureBuildUpResult>;
57
+ ```
58
+
59
+ This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
60
+
61
+ ## SimulateExposureBuildUpFrame
62
+
63
+ One readout of a build-up.
64
+
65
+ ```ts
66
+ export interface SimulateExposureBuildUpFrame {
67
+ /** The fraction of the exposure collected. */
68
+ fraction: number;
69
+ /** fraction × the request's shutter time, in s. */
70
+ elapsedSeconds: number;
71
+ /**
72
+ * The fraction developed as its own photo. At fraction 1 it is `simulateCapture(options)` itself, byte for byte. Below 1 it is
73
+ * the result of the fraction's capture (manual, the whole exposure's f-number and ISO, shutter fraction·t: its `image`, `raw`,
74
+ * `plan` and `request`), with the build-up's counts, the `exposure.buildUp` assumption, and the whole exposure's `exposure` and
75
+ * `sensor` reports (the camera's settings and noise figure, not the elapsed part's).
76
+ */
77
+ result: SimulateCaptureResult;
78
+ }
79
+ ```
80
+
81
+ ## SimulateExposureBuildUpOptions
82
+
83
+ The build-up to render with {@link simulateExposureBuildUp}.
84
+
85
+ ```ts
86
+ export interface SimulateExposureBuildUpOptions {
87
+ /**
88
+ * Fractions of `exposure.shutterSeconds` to read out, strictly increasing, each above 0 and at most 1 (1 to 64 of them), for
89
+ * example `[0.2, 0.4, 0.6, 0.8, 1]`. A fraction of 1 is the whole exposure: `simulateCapture` of the request.
90
+ */
91
+ fractions: readonly number[];
92
+ }
93
+ ```
94
+
95
+ ## SimulateExposureBuildUpResult
96
+
97
+ The readouts of {@link simulateExposureBuildUp}, in the order of `fractions`.
98
+
99
+ ```ts
100
+ export interface SimulateExposureBuildUpResult {
101
+ /** The whole exposure's shutter time, in s. */
102
+ shutterSeconds: number;
103
+ frames: readonly SimulateExposureBuildUpFrame[];
104
+ }
105
+ ```
@@ -0,0 +1,99 @@
1
+ # facade/fast-methods.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## SIMULATE_CAPTURE_FAST_METHODS
6
+
7
+ 2.20.0: every speed-versus-accuracy choice of the capture facade and the display view (see the module comment).
8
+
9
+ ```ts
10
+ SIMULATE_CAPTURE_FAST_METHODS: readonly SimulateCaptureFastMethod[] = Object.freeze([
11
+ { id: "lens.catalogue/field-grid", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateCapture"],
12
+ fast: "#568: the PSF blended over a field grid and tabulated expected electrons, verified per CFA phase against the exact chain",
13
+ exact: "the environment PSF quadrature with the PSF resolved at each query point", followsFormatDefault: true,
14
+ resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "fast.lens.catalogue" } },
15
+ { id: "lens.catalogue/cats-eye", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
16
+ fast: "#567: with lens.defocus, the lens's bokeh without its cat's eye (the axis kernel everywhere)",
17
+ exact: "the cat's eye shaped at each field point (fast wides and normals)", followsFormatDefault: true,
18
+ resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "fast.lens.catalogue.catsEye" } },
19
+ { id: "lens.catalogue/view-psf-zones", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
20
+ fast: "#569 (H1): 64 field zones along the longer side, each with the PSF at its centre",
21
+ exact: "one zone per site: the PSF at each site centre", followsFormatDefault: true,
22
+ resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "view.lensPsf" } },
23
+ { id: "lens.diffraction/rolling-shutter-taps", override: "lens.diffraction", methods: "fast-and-exact", runs: ["simulateCapture"],
24
+ fast: "#565: under rolling-shutter rotation (#551: or subject motion), each PSF tap reads the scene at its own source point",
25
+ exact: "each tap reads the scene at its site-lattice point (ties on chart boundaries resolve as on the lattice)", followsFormatDefault: true,
26
+ resolver: { file: CAPTURE, call: "resolveFast(diffractionFast, fastTop)" }, report: { applied: "lens.diffraction", assumption: "fast.lens.diffraction" } },
27
+ { id: "lens.catalogue/rolling-shutter-taps", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateCapture"],
28
+ fast: "#588: under rolling-shutter rotation, each catalogue PSF tap reads the scene at its own source point",
29
+ exact: "each tap reads the scene at its site-lattice point (ties on chart boundaries resolve as on the lattice)", followsFormatDefault: true,
30
+ resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "fast.lens.catalogue" } },
31
+ { id: "output.demosaic.ahd/view-low-signal-correction", override: "output.demosaic", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
32
+ fast: "(H3) 2048 draws per correction state, applied where a pixel's linear noise exceeds 0.01",
33
+ exact: "8192 draws per state, at the same 0.01 threshold", followsFormatDefault: true,
34
+ resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd", assumption: "view.demosaic" } },
35
+ { id: "view.uniform-pixel-sampling", override: "fast", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
36
+ fast: "(H2) a quarter of the samples per axis in display pixels whose pilot finds no detail",
37
+ exact: "the full samples per axis in every display pixel", followsFormatDefault: true,
38
+ resolver: { file: CAPTURE, call: "uniformSampling: fastTop" }, report: { assumption: "view.estimation" } },
39
+ { id: "prepareDisplayView.gpu", override: "fast", methods: "fast-and-exact", runs: ["prepareDisplayView"],
40
+ fast: "gpu \"auto\": WebGPU float32 where a conforming device supports the request (a bounded approximation), else the CPU reference",
41
+ exact: "gpu \"off\": the CPU reference; an explicit gpu wins", followsFormatDefault: true,
42
+ resolver: { file: VIEW, call: "parsePrepareDisplayViewOptions(prepareOptions, (options as { fast?: unknown } | null | undefined)?.fast !== false)" },
43
+ report: { field: "diagnostics.fast.execution" } },
44
+ { id: "sensor.midGrey", override: "fast", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
45
+ fast: "#535: the mid-gray noise figure is not computed (about 0.3 ms)", exact: "result.sensor.midGrey is computed", followsFormatDefault: true,
46
+ resolver: { file: CAPTURE, call: "fastTop ? undefined : 0" }, report: { field: "sensor.midGrey" } },
47
+ { id: "motion.subject/temporal-midpoints", override: "motion.subject", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
48
+ fast: "#551: with the default count and a still camera, one temporal midpoint per site of the subject's streak plus one, when that is fewer than 8",
49
+ exact: "the default 8 temporal midpoints (an explicit sensor.temporalSampleCount always wins)", followsFormatDefault: true,
50
+ resolver: { file: CAPTURE, call: "resolveFast(subjectFast, fastTop)" }, report: { applied: "motion.subject", assumption: "fast.motion.subject" } },
51
+ { id: "lens.diffraction", override: "lens.diffraction", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
52
+ fast: "", exact: "the diffraction kernels (a fast method only under rolling-shutter rotation, above)", followsFormatDefault: true,
53
+ resolver: { file: CAPTURE, call: "resolveFast(diffractionFast, fastTop)" }, report: { applied: "lens.diffraction" } },
54
+ { id: "sensor.antiAliasing", override: "sensor.antiAliasing", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
55
+ fast: "", exact: "the point-splitting filter", followsFormatDefault: true,
56
+ resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.antiAliasing" } },
57
+ { id: "sensor.pixelAperture.box", override: "sensor.pixelAperture", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
58
+ fast: "", exact: "the N × N midpoint rule", followsFormatDefault: true,
59
+ resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.pixelAperture.box" } },
60
+ { id: "output.demosaic.ahd", override: "output.demosaic", methods: "exact-only", runs: ["simulateCapture"],
61
+ fast: "", exact: "the AHD demosaic of the capture (a fast method only in the display view's correction, above)", followsFormatDefault: true,
62
+ resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd" } }
63
+ ] as const satisfies readonly SimulateCaptureFastMethod[])
64
+ ```
65
+
66
+ ## SimulateCaptureFastMethod
67
+
68
+ One speed-versus-accuracy choice of {@link SIMULATE_CAPTURE_FAST_METHODS} (2.20.0).
69
+
70
+ ```ts
71
+ export interface SimulateCaptureFastMethod {
72
+ /** A short id: the option path and, where one option has several fast methods, the method. */
73
+ id: string;
74
+ /**
75
+ * The request path whose record takes the `fast` override (`allowKeys` path in simulate-capture.ts), or "fast" when only the
76
+ * top-level flag sets it (a choice with no option record of its own).
77
+ */
78
+ override: string;
79
+ /** "fast-and-exact": both methods exist; "exact-only": the record takes `fast`, but there is no fast method yet (reports false). */
80
+ methods: "fast-and-exact" | "exact-only";
81
+ /** Where it applies. */
82
+ runs: readonly ("simulateCapture" | "simulateDisplayView" | "prepareDisplayView")[];
83
+ /** The fast method and the exact one, in words (empty for exact-only). */
84
+ fast: string;
85
+ exact: string;
86
+ /** Always true: no choice keeps its own default (the test checks it). */
87
+ followsFormatDefault: true;
88
+ /**
89
+ * The resolver call in the source (`resolveFast(<record flag>, fastTop)`, or `fastTop` itself for a top-level choice), which the
90
+ * test finds in the named file.
91
+ */
92
+ resolver: { file: string; call: string };
93
+ /**
94
+ * How the result reports it: the `diagnostics.fast.applied` key (true where the fast method ran), an assumption id the fast
95
+ * method adds, or another result field. At least one; the test finds each assumption id in the source.
96
+ */
97
+ report: { applied?: string; assumption?: string; field?: string };
98
+ }
99
+ ```
@@ -0,0 +1,16 @@
1
+ # facade/image-plane-enable.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## prepareImagePlaneScenes
6
+
7
+ 2.21 (#581): enables `{ kind: "image-plane" }` scenes (a photo as a flat print, docs/IMAGE_PLANE.md) in this JavaScript realm by
8
+ loading their module (a lazy chunk of about 14 KB gzip with its lattice data). Call it once, in the worker that captures, before
9
+ the first request with such a scene; later calls resolve at once. A request with an image-plane scene before it fails with a
10
+ message saying so.
11
+
12
+ ```ts
13
+ export async function prepareImagePlaneScenes(): Promise<void>;
14
+ ```
15
+
16
+ This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
@@ -0,0 +1,67 @@
1
+ # facade/mixed-illumination.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## MixedIllumination
6
+
7
+ 2.22 (#550), `simulateCapture` `illumination`: sources blended across the chart and point sources with inverse-square falloff
8
+ (docs/MIXED_ILLUMINATION.md). It replaces `illuminant` and `illuminanceLux`. `sources` lists 1 to 8 sources (with two or more, a
9
+ `blend` is required; one lights the whole plane); `points` lists 1 to 8 point sources; a request needs two or more sources or at least one point. Blend
10
+ coordinates are chart-local with `chartSize` and frame units without it; (0, 0) is the frame center.
11
+
12
+ ```ts
13
+ export interface MixedIllumination {
14
+ sources?: readonly MixedIlluminationSource[];
15
+ blend?: MixedIlluminationBlend;
16
+ points?: readonly MixedIlluminationPointSource[];
17
+ }
18
+ ```
19
+
20
+ ## MixedIlluminationBlend
21
+
22
+ How the sources blend across the chart. `linear-gradient` and `radial` map a point to a position t in [0, 1] (its projection
23
+ onto `from` → `to`, clamped; or its distance from `center` over `radius`, clamped) and crossfade linearly between consecutive
24
+ sources at their `stops` (one per source, non-decreasing in [0, 1]; default evenly spaced from 0 to 1): source 0 alone up to
25
+ stops[0], the last source alone from its stop on, and two equal stops a hard edge (the later source from the stop on).
26
+ `regions` gives each point the source of the first rectangle that contains it (bounds inclusive) and `outsideSource` elsewhere.
27
+
28
+ ```ts
29
+ export type MixedIlluminationBlend =
30
+ | { kind: "linear-gradient"; from: MixedIlluminationPoint; to: MixedIlluminationPoint; stops?: readonly number[] }
31
+ | { kind: "radial"; center?: MixedIlluminationPoint; radius: number; stops?: readonly number[] }
32
+ | { kind: "regions"; regions: readonly { source: number; uMin: number; uMax: number; vMin: number; vMax: number }[]; outsideSource: number };
33
+ ```
34
+
35
+ ## MixedIlluminationPoint
36
+
37
+ A point in chart coordinates (see {@link MixedIllumination}).
38
+
39
+ ```ts
40
+ export interface MixedIlluminationPoint { u: number; v: number }
41
+ ```
42
+
43
+ ## MixedIlluminationPointSource
44
+
45
+ 2.22 (#550): a point light on the scene plane's front side: an illuminant spectrum, a position in metres (`xM`, `yM` on the plane
46
+ from the optical axis, +x right and +y up as seen from the camera; `heightM` > 0 in front of the plane, toward the camera) and an
47
+ intensity, either in candela or as the illuminance it gives at normal incidence at a reference distance (I = E·r²).
48
+
49
+ ```ts
50
+ export interface MixedIlluminationPointSource {
51
+ illuminant: CaptureIlluminant;
52
+ position: { xM: number; yM: number; heightM: number };
53
+ intensity: { candela: number } | { illuminanceLux: number; distanceM: number };
54
+ }
55
+ ```
56
+
57
+ ## MixedIlluminationSource
58
+
59
+ One light source of a mixed illumination: an illuminant (as `simulateCapture` `illuminant` takes it) at an illuminance.
60
+
61
+ ```ts
62
+ export interface MixedIlluminationSource {
63
+ illuminant: CaptureIlluminant;
64
+ /** Illuminance this source alone gives where its weight is 1, lux, (0, 1e7]. */
65
+ illuminanceLux: number;
66
+ }
67
+ ```
@@ -0,0 +1,131 @@
1
+ # facade/representative-light-sources.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## createRepresentativeLightSourceIlluminant
6
+
7
+ A capture illuminant for a source: `{ kind: "spectrum", spectrum, label }` with the spectrum of
8
+ {@link createRepresentativeLightSourceSpectrum} and the label "<name> (representative, not a named product)".
9
+
10
+ ```ts
11
+ export function createRepresentativeLightSourceIlluminant(input: { sourceId: RepresentativeLightSourceId }): Extract<CaptureIlluminant, { kind: "spectrum" }>;
12
+ ```
13
+
14
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
15
+
16
+ ## createRepresentativeLightSourceSpectrum
17
+
18
+ A source's relative spectral power distribution S(λ) = b + Σ aₖ·exp(−½((λ − μₖ)/σₖ)²) as a function spectrum on
19
+ 360–830 nm (relative units; values memoized per wavelength). Pass it to a capture as
20
+ `illuminant: { kind: "spectrum", spectrum }`; for color work use `sensor.spectralStepNanometers: 5` or finer
21
+ (docs/LIGHT_SOURCES.md, "Quadrature"). Throws `InvalidScientificInputError` outside 360–830 nm.
22
+
23
+ ```ts
24
+ export function createRepresentativeLightSourceSpectrum(input: { sourceId: RepresentativeLightSourceId }): ColorimetricSpectrum;
25
+ ```
26
+
27
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
28
+
29
+ ## getRepresentativeLightSourceSet
30
+
31
+ The built-in representative light sources: Photivra's own parametric spectra of a tri-phosphor fluorescent tube and
32
+ three white LEDs (low and high color rendering at 4000 K, warm at 2700 K). A static, deeply frozen engine contract.
33
+ Each is "representative, not a named product"; docs/LIGHT_SOURCES.md gives every formula and the reasons for it.
34
+
35
+ ```ts
36
+ export function getRepresentativeLightSourceSet(): RepresentativeLightSourceSet;
37
+ ```
38
+
39
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
40
+
41
+ ## REPRESENTATIVE_LIGHT_SOURCE_IDS
42
+
43
+ The source ids, in set order.
44
+
45
+ ```ts
46
+ REPRESENTATIVE_LIGHT_SOURCE_IDS = Object.freeze([
47
+ "tri-phosphor-fluorescent-4000k", "white-led-low-rendering-4000k", "white-led-high-rendering-4000k", "warm-white-led-2700k"
48
+ ] as const)
49
+ ```
50
+
51
+ ## REPRESENTATIVE_LIGHT_SOURCE_SET_ID
52
+
53
+ Identity of the built-in light-source set.
54
+
55
+ ```ts
56
+ REPRESENTATIVE_LIGHT_SOURCE_SET_ID = "photivra-representative-light-sources"
57
+ ```
58
+
59
+ ## REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION
60
+
61
+ Version of the set's spectra; a change to any spectrum changes it.
62
+
63
+ ```ts
64
+ REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION = "1.0.0"
65
+ ```
66
+
67
+ ## REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS
68
+
69
+ Domain of every spectrum, nm (the CIE 1931 observer's domain; the capture facade uses 380–780 nm of it).
70
+
71
+ ```ts
72
+ REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS = Object.freeze({ minimum: 360, maximum: 830 })
73
+ ```
74
+
75
+ ## RepresentativeLightSource
76
+
77
+ One built-in source: its id, name, design reason and spectrum definition.
78
+
79
+ ```ts
80
+ export interface RepresentativeLightSource {
81
+ sourceId: RepresentativeLightSourceId;
82
+ /** Photivra's descriptive name, for example "White LED, high color rendering, 4000 K". */
83
+ name: string;
84
+ family: "fluorescent" | "led";
85
+ /** The CCT the spectrum was designed for, K (its computed CCT is within 1 K of it, Duv within 0.0001; docs/LIGHT_SOURCES.md). */
86
+ nominalCorrelatedColorTemperatureKelvin: number;
87
+ /** Always "representative, not a named product". */
88
+ label: "representative, not a named product";
89
+ /** One sentence: what the source represents and why it is built this way. */
90
+ reason: string;
91
+ /** The constant b (a low continuum), relative units. */
92
+ base: number;
93
+ bands: readonly RepresentativeLightSourceBand[];
94
+ }
95
+ ```
96
+
97
+ ## RepresentativeLightSourceBand
98
+
99
+ One Gaussian band a·exp(−½((λ − μ)/σ)²) of a source spectrum.
100
+
101
+ ```ts
102
+ export interface RepresentativeLightSourceBand {
103
+ kind: "gaussian";
104
+ /** What the band represents, for example "blue pump" or "green phosphor line". */
105
+ role: string;
106
+ amplitude: number;
107
+ centerNanometers: number;
108
+ standardDeviationNanometers: number;
109
+ }
110
+ ```
111
+
112
+ ## RepresentativeLightSourceId
113
+
114
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
115
+
116
+ ```ts
117
+ export type RepresentativeLightSourceId = (typeof REPRESENTATIVE_LIGHT_SOURCE_IDS)[number];
118
+ ```
119
+
120
+ ## RepresentativeLightSourceSet
121
+
122
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
123
+
124
+ ```ts
125
+ export interface RepresentativeLightSourceSet {
126
+ setId: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_ID;
127
+ setVersion: typeof REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION;
128
+ wavelengthRangeNanometers: { readonly minimum: number; readonly maximum: number };
129
+ sources: readonly RepresentativeLightSource[];
130
+ }
131
+ ```
@@ -91,7 +91,8 @@ provider `providerProfileId`.
91
91
 
92
92
  ```ts
93
93
  export function createPlanarSceneRadianceProvider(input: {
94
- scene: ChartScene | ChartMaterialLayerScene | CallerReflectanceScene | CallerRadianceScene | CallerMaterialScene;
94
+ /** An `image-plane` scene (#581) is refused: it is taken by the facade's capture and display-view functions, which load its module. */
95
+ scene: ChartScene | ChartMaterialLayerScene | CallerReflectanceScene | CallerRadianceScene | CallerMaterialScene | ImagePlaneScene;
95
96
  illuminant: CaptureIlluminant;
96
97
  illuminanceLux: number;
97
98
  distanceM: number;
@@ -110,6 +111,47 @@ export function createPlanarSceneRadianceProvider(input: {
110
111
 
111
112
  This call is synchronous; any supplied provider must follow its explicit synchronous contract.
112
113
 
114
+ ## ImagePlaneImage
115
+
116
+ 2.21 (#581): an image the caller supplies, for an {@link ImagePlaneScene}. `data` holds `width × height` pixels row by row
117
+ from the top, `channels` (3: RGB, 4: RGBA) samples each, in the encoding's type: `srgb-8` Uint8Array or Uint8ClampedArray
118
+ (canvas `ImageData.data`), `srgb-16` Uint16Array, `linear-srgb` Float32Array. sRGB primaries and white (IEC 61966-2-1) in
119
+ every encoding; `srgb-8` and `srgb-16` are decoded with the sRGB transfer function, `linear-srgb` is already linear and is
120
+ clipped to [0, 1] (the clipped samples are counted). Alpha is linear coverage over the paper. The array is read, never
121
+ changed or kept beyond the call.
122
+
123
+ ```ts
124
+ export interface ImagePlaneImage {
125
+ width: number;
126
+ height: number;
127
+ channels: 3 | 4;
128
+ encoding: "srgb-8" | "srgb-16" | "linear-srgb";
129
+ data: Uint8Array | Uint8ClampedArray | Uint16Array | Float32Array;
130
+ }
131
+ ```
132
+
133
+ ## ImagePlaneScene
134
+
135
+ 2.21 (#581): a photo as a flat matte print on the scene plane, lit by the request's illuminant like a chart. Each pixel's
136
+ color becomes a reflectance spectrum through the engine's print reflectance lattice (docs/IMAGE_PLANE.md): an
137
+ approximation, a print of an image that was already processed, clipped and blurred by the camera that took it. The print's
138
+ physical size is `chartSize` (its height defaults to the image's aspect); without it the print is fitted inside the frame,
139
+ centered, on the 18 % surround.
140
+
141
+ ```ts
142
+ export interface ImagePlaneScene {
143
+ kind: "image-plane";
144
+ sceneId: string;
145
+ image: ImagePlaneImage;
146
+ /** The paper and the deepest ink: reflectances of sRGB white and black, 0 ≤ black < white ≤ 1. Defaults 0.90 and 0.01. */
147
+ paper?: { whiteReflectance?: number; blackReflectance?: number };
148
+ /** How the print is reconstructed between pixel centers. Default `bilinear` (the pixels' spectra, bilinearly weighted). */
149
+ interpolation?: "bilinear" | "nearest";
150
+ /** Opt-in raised pixel limit: an integer up to the full-resolution ceiling. Default 24 000 000 pixels. */
151
+ maximumPixels?: number;
152
+ }
153
+ ```
154
+
113
155
  ## PHOTOPIC_MAXIMUM_LUMINOUS_EFFICACY_LM_PER_W
114
156
 
115
157
  CIE maximum luminous efficacy for photopic vision, lm/W.
@@ -128,8 +170,11 @@ export interface PlanarSceneRadianceProvider {
128
170
  providerProfileId: string;
129
171
  evaluateRadiance(request: Readonly<SceneRadianceEvaluationRequest>): SceneRadianceEvaluationResult;
130
172
  evaluateApertureRadiance(request: Readonly<SceneRadianceEvaluationRequest>, ray: Readonly<SensorApertureRay>): SceneRadianceEvaluationResult;
131
- /** Spectral radiance at a plane point (frame units), the same value the callbacks return. */
132
- radianceAt(u: number, v: number, wavelengthNanometers: number): number;
173
+ /**
174
+ * Spectral radiance at a plane point (frame units), the same value the callbacks return. #551: a moving subject's provider
175
+ * reads it at time `t` (seconds from the first opening, default 0); a still scene ignores it.
176
+ */
177
+ radianceAt(u: number, v: number, wavelengthNanometers: number, t?: number): number;
133
178
  /**
134
179
  * 2.3, chart scenes only: the chart material at a plane point (frame units), or `materials.length` for the
135
180
  * surround of a fixed-size chart; `materialRadiance` gives that material's spectral radiance, the same value
@@ -137,6 +182,12 @@ export interface PlanarSceneRadianceProvider {
137
182
  */
138
183
  chartMaterialAt?(u: number, v: number): number;
139
184
  materialRadiance?(material: number, wavelengthNanometers: number): number;
185
+ /**
186
+ * 2.21 (#581), image-plane scenes only: the tabulated expected signal's scene at these spectral nodes and channel responses, its
187
+ * radiance per node and, for the direct path, Σ_n A_c(λ_n)·L(λ_n) integrated from each pixel's reconstructed spectrum
188
+ * (`ResolvedImagePlaneScene.pixelSignals`, the surround's reflectance times the neutral signal outside the print).
189
+ */
190
+ imageSignalScene?(wavelengths: readonly number[], perNode: readonly Float64Array[]): ImagePlaneSignalScene;
140
191
  }
141
192
  ```
142
193