@photivra/engine 2.18.0 → 2.20.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 (335) hide show
  1. package/CHANGELOG.md +179 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +7 -5
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +14 -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/focus-control.js +2 -75
  11. package/dist/capture/focus-control.js.map +1 -1
  12. package/dist/capture/native-environment-lean.d.ts +1 -0
  13. package/dist/capture/native-environment-lean.js +2 -6
  14. package/dist/capture/native-environment-lean.js.map +1 -1
  15. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  16. package/dist/capture/native-environment-optical-stage.js +2 -5
  17. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  18. package/dist/capture/native-environment-raw.js +1 -5
  19. package/dist/capture/native-environment-raw.js.map +1 -1
  20. package/dist/capture/native-raw-dng.d.ts +6 -0
  21. package/dist/capture/native-raw-dng.js +16 -2
  22. package/dist/capture/native-raw-dng.js.map +1 -1
  23. package/dist/capture/release-sequence.js +2 -47
  24. package/dist/capture/release-sequence.js.map +1 -1
  25. package/dist/capture/sensor-raw-producer.js +3 -5
  26. package/dist/capture/sensor-raw-producer.js.map +1 -1
  27. package/dist/color/white-balance.d.ts +2 -0
  28. package/dist/color/white-balance.js +3 -56
  29. package/dist/color/white-balance.js.map +1 -1
  30. package/dist/composition/image-formation-plan.d.ts +1 -0
  31. package/dist/composition/image-formation-plan.js +3 -49
  32. package/dist/composition/image-formation-plan.js.map +1 -1
  33. package/dist/core/evidence-provenance.js +1 -16
  34. package/dist/core/evidence-provenance.js.map +1 -1
  35. package/dist/core/record-validation.d.ts +54 -0
  36. package/dist/core/record-validation.js +176 -0
  37. package/dist/core/record-validation.js.map +1 -1
  38. package/dist/core/scientific-assurance.js +1 -18
  39. package/dist/core/scientific-assurance.js.map +1 -1
  40. package/dist/core/shared-helpers.d.ts +35 -0
  41. package/dist/core/shared-helpers.js +38 -0
  42. package/dist/core/shared-helpers.js.map +1 -0
  43. package/dist/core/spectral-composition.js +1 -36
  44. package/dist/core/spectral-composition.js.map +1 -1
  45. package/dist/core/spectral.js +1 -19
  46. package/dist/core/spectral.js.map +1 -1
  47. package/dist/core/version.d.ts +1 -1
  48. package/dist/core/version.js +1 -1
  49. package/dist/core/version.js.map +1 -1
  50. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  51. package/dist/equipment/exposure-capabilities.js +2 -36
  52. package/dist/equipment/exposure-capabilities.js.map +1 -1
  53. package/dist/equipment/generic-lens-catalogue.d.ts +8 -2
  54. package/dist/equipment/generic-lens-catalogue.js +31 -2
  55. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  56. package/dist/equipment/generic-tier-assets.js +2 -3
  57. package/dist/equipment/generic-tier-assets.js.map +1 -1
  58. package/dist/equipment/generic-tier-presets.js +2 -3
  59. package/dist/equipment/generic-tier-presets.js.map +1 -1
  60. package/dist/equipment/iso-capabilities.js +2 -49
  61. package/dist/equipment/iso-capabilities.js.map +1 -1
  62. package/dist/equipment/metering-capabilities.js +1 -26
  63. package/dist/equipment/metering-capabilities.js.map +1 -1
  64. package/dist/equipment/release-capabilities.js +1 -53
  65. package/dist/equipment/release-capabilities.js.map +1 -1
  66. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  67. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  68. package/dist/exposure/exposure-duration-control.js +1 -16
  69. package/dist/exposure/exposure-duration-control.js.map +1 -1
  70. package/dist/exposure/exposure-mode-resolver.js +1 -17
  71. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  72. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  73. package/dist/exposure/filtered-exposure-time.js +129 -0
  74. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  75. package/dist/exposure/flash-ttl.d.ts +1 -0
  76. package/dist/exposure/flash-ttl.js +1 -1
  77. package/dist/exposure/flash-ttl.js.map +1 -1
  78. package/dist/exposure/flash.js +1 -49
  79. package/dist/exposure/flash.js.map +1 -1
  80. package/dist/exposure/metering-scene-radiance.js +1 -25
  81. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  82. package/dist/exposure/metering-target.js +1 -30
  83. package/dist/exposure/metering-target.js.map +1 -1
  84. package/dist/exposure/metering-temporal.js +1 -8
  85. package/dist/exposure/metering-temporal.js.map +1 -1
  86. package/dist/exposure/metering.js +1 -72
  87. package/dist/exposure/metering.js.map +1 -1
  88. package/dist/exposure/program-line.js +1 -45
  89. package/dist/exposure/program-line.js.map +1 -1
  90. package/dist/facade/develop-joined-parts.d.ts +29 -0
  91. package/dist/facade/develop-joined-parts.js +33 -0
  92. package/dist/facade/develop-joined-parts.js.map +1 -0
  93. package/dist/facade/display-view-gpu.d.ts +7 -4
  94. package/dist/facade/display-view-gpu.js +4 -4
  95. package/dist/facade/display-view-gpu.js.map +1 -1
  96. package/dist/facade/display-view-lens-signal.d.ts +1 -1
  97. package/dist/facade/display-view-lens-signal.js +5 -3
  98. package/dist/facade/display-view-lens-signal.js.map +1 -1
  99. package/dist/facade/display-view.d.ts +3 -1
  100. package/dist/facade/display-view.js +1 -1
  101. package/dist/facade/display-view.js.map +1 -1
  102. package/dist/facade/fast-methods.d.ts +50 -0
  103. package/dist/facade/fast-methods.js +50 -0
  104. package/dist/facade/fast-methods.js.map +1 -0
  105. package/dist/facade/fast-resolution.d.ts +12 -0
  106. package/dist/facade/fast-resolution.js +18 -0
  107. package/dist/facade/fast-resolution.js.map +1 -0
  108. package/dist/facade/generic-lens-capture.d.ts +6 -3
  109. package/dist/facade/generic-lens-capture.js +6 -3
  110. package/dist/facade/generic-lens-capture.js.map +1 -1
  111. package/dist/facade/simulate-capture.d.ts +88 -16
  112. package/dist/facade/simulate-capture.js +279 -153
  113. package/dist/facade/simulate-capture.js.map +1 -1
  114. package/dist/facade/simulate-display-view.d.ts +3 -2
  115. package/dist/facade/simulate-display-view.js +22 -12
  116. package/dist/facade/simulate-display-view.js.map +1 -1
  117. package/dist/gpu/chart-composite-gpu.js +1 -3
  118. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  119. package/dist/gpu/chart-radiance-gpu.js +1 -5
  120. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  121. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  122. package/dist/gpu/display-view-quality-gpu.js +4 -8
  123. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  124. package/dist/gpu/display-view-speed-gpu.js +3 -25
  125. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  126. package/dist/gpu/sensor-stage-gpu.js +1 -5
  127. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  128. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  129. package/dist/gpu/webgpu-runtime.js +3 -0
  130. package/dist/gpu/webgpu-runtime.js.map +1 -1
  131. package/dist/index.d.ts +11 -1
  132. package/dist/index.js +8 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/motion/camera-rotation.js +1 -6
  135. package/dist/motion/camera-rotation.js.map +1 -1
  136. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  137. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  138. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  139. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  140. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  141. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  142. package/dist/motion/capture-rotation-trajectory.js +2 -17
  143. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  144. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  145. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  146. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  147. package/dist/motion/extended-object-projection.js +5 -40
  148. package/dist/motion/extended-object-projection.js.map +1 -1
  149. package/dist/optics/aperture-diameter.d.ts +47 -0
  150. package/dist/optics/aperture-diameter.js +50 -0
  151. package/dist/optics/aperture-diameter.js.map +1 -0
  152. package/dist/optics/atmospheric-haze.js +1 -7
  153. package/dist/optics/atmospheric-haze.js.map +1 -1
  154. package/dist/optics/brown-conrady-distortion.js +1 -7
  155. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  156. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  157. package/dist/optics/complex-pupil-psf.js +2 -84
  158. package/dist/optics/complex-pupil-psf.js.map +1 -1
  159. package/dist/optics/detail-limit.d.ts +182 -0
  160. package/dist/optics/detail-limit.js +336 -0
  161. package/dist/optics/detail-limit.js.map +1 -0
  162. package/dist/optics/focus-state.js +1 -11
  163. package/dist/optics/focus-state.js.map +1 -1
  164. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  165. package/dist/optics/front-of-lens-filter.js +3 -65
  166. package/dist/optics/front-of-lens-filter.js.map +1 -1
  167. package/dist/optics/generic-lens-psf.d.ts +18 -2
  168. package/dist/optics/generic-lens-psf.js +25 -2
  169. package/dist/optics/generic-lens-psf.js.map +1 -1
  170. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  171. package/dist/optics/generic-nd-filter-presets.js +48 -0
  172. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  173. package/dist/optics/illumination-vignetting.js +1 -6
  174. package/dist/optics/illumination-vignetting.js.map +1 -1
  175. package/dist/optics/lens-psf-profile.d.ts +4 -1
  176. package/dist/optics/lens-psf-profile.js +12 -111
  177. package/dist/optics/lens-psf-profile.js.map +1 -1
  178. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  179. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  180. package/dist/optics/pupil-vignetting.js +1 -7
  181. package/dist/optics/pupil-vignetting.js.map +1 -1
  182. package/dist/optics/radial-distortion.js +1 -6
  183. package/dist/optics/radial-distortion.js.map +1 -1
  184. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  185. package/dist/optics/scene-to-sensor-irradiance.js +11 -96
  186. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  187. package/dist/optics/stray-light.d.ts +1 -0
  188. package/dist/optics/stray-light.js +1 -1
  189. package/dist/optics/stray-light.js.map +1 -1
  190. package/dist/optics/zernike-wavefront.js +1 -7
  191. package/dist/optics/zernike-wavefront.js.map +1 -1
  192. package/dist/output/capture-geometry.js +1 -8
  193. package/dist/output/capture-geometry.js.map +1 -1
  194. package/dist/output/crop-to-match-focal-length.d.ts +62 -0
  195. package/dist/output/crop-to-match-focal-length.js +63 -0
  196. package/dist/output/crop-to-match-focal-length.js.map +1 -0
  197. package/dist/output/developed-lens-corrections.d.ts +62 -0
  198. package/dist/output/developed-lens-corrections.js +135 -0
  199. package/dist/output/developed-lens-corrections.js.map +1 -0
  200. package/dist/output/generic-lens-corrections.d.ts +5 -7
  201. package/dist/output/generic-lens-corrections.js +11 -10
  202. package/dist/output/generic-lens-corrections.js.map +1 -1
  203. package/dist/output/geometric-transforms.js +1 -4
  204. package/dist/output/geometric-transforms.js.map +1 -1
  205. package/dist/output/lens-corrections.d.ts +12 -2
  206. package/dist/output/lens-corrections.js +22 -7
  207. package/dist/output/lens-corrections.js.map +1 -1
  208. package/dist/output/native-capture-sdr.d.ts +2 -2
  209. package/dist/output/native-capture-sdr.js +3 -2
  210. package/dist/output/native-capture-sdr.js.map +1 -1
  211. package/dist/output/print-contrast-reference.js +1 -1
  212. package/dist/output/print-contrast-reference.js.map +1 -1
  213. package/dist/output/print-size-blur-noise.d.ts +110 -0
  214. package/dist/output/print-size-blur-noise.js +138 -0
  215. package/dist/output/print-size-blur-noise.js.map +1 -0
  216. package/dist/processing/development-detail.d.ts +29 -0
  217. package/dist/processing/development-detail.js +128 -107
  218. package/dist/processing/development-detail.js.map +1 -1
  219. package/dist/schema/illumination-temporal.js +3 -72
  220. package/dist/schema/illumination-temporal.js.map +1 -1
  221. package/dist/schema/illumination.d.ts +1 -0
  222. package/dist/schema/illumination.js +2 -65
  223. package/dist/schema/illumination.js.map +1 -1
  224. package/dist/schema/scene-radiance-temporal.js +2 -14
  225. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  226. package/dist/schema/scene-radiance.js +2 -60
  227. package/dist/schema/scene-radiance.js.map +1 -1
  228. package/dist/schema/validation.d.ts +2 -0
  229. package/dist/schema/validation.js +2 -17
  230. package/dist/schema/validation.js.map +1 -1
  231. package/dist/sensor/accumulated-charge.js +2 -59
  232. package/dist/sensor/accumulated-charge.js.map +1 -1
  233. package/dist/sensor/architecture.js +1 -16
  234. package/dist/sensor/architecture.js.map +1 -1
  235. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  236. package/dist/sensor/camera-saturation-capacity.js +3 -55
  237. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  238. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  239. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  240. package/dist/sensor/capture-mode-timing.js +1 -39
  241. package/dist/sensor/capture-mode-timing.js.map +1 -1
  242. package/dist/sensor/capture-mode.js +1 -23
  243. package/dist/sensor/capture-mode.js.map +1 -1
  244. package/dist/sensor/color-sampling.js +1 -26
  245. package/dist/sensor/color-sampling.js.map +1 -1
  246. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  247. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  248. package/dist/sensor/dark-current.js +2 -45
  249. package/dist/sensor/dark-current.js.map +1 -1
  250. package/dist/sensor/eqe-electron-rate.js +2 -41
  251. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  252. package/dist/sensor/exposure-window.d.ts +7 -0
  253. package/dist/sensor/exposure-window.js +9 -24
  254. package/dist/sensor/exposure-window.js.map +1 -1
  255. package/dist/sensor/iso-gain.js +1 -7
  256. package/dist/sensor/iso-gain.js.map +1 -1
  257. package/dist/sensor/iso-signal-chain.js +1 -43
  258. package/dist/sensor/iso-signal-chain.js.map +1 -1
  259. package/dist/sensor/optical-stack.js +1 -25
  260. package/dist/sensor/optical-stack.js.map +1 -1
  261. package/dist/sensor/photon-energy.js +1 -26
  262. package/dist/sensor/photon-energy.js.map +1 -1
  263. package/dist/sensor/physical-charge-capacity.js +3 -77
  264. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  265. package/dist/sensor/radiometry-readiness.js +2 -23
  266. package/dist/sensor/radiometry-readiness.js.map +1 -1
  267. package/dist/sensor/raw-readout.d.ts +3 -0
  268. package/dist/sensor/raw-readout.js +4 -66
  269. package/dist/sensor/raw-readout.js.map +1 -1
  270. package/dist/sensor/raw-reconstruction.js +2 -64
  271. package/dist/sensor/raw-reconstruction.js.map +1 -1
  272. package/dist/sensor/readout-exposure-linkage.js +1 -9
  273. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  274. package/dist/sensor/readout-timing.js +4 -79
  275. package/dist/sensor/readout-timing.js.map +1 -1
  276. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  277. package/dist/sensor/response-application-compatibility.js +4 -35
  278. package/dist/sensor/response-application-compatibility.js.map +1 -1
  279. package/dist/sensor/response-operating-range.d.ts +1 -0
  280. package/dist/sensor/response-operating-range.js +3 -79
  281. package/dist/sensor/response-operating-range.js.map +1 -1
  282. package/dist/sensor/responsivity-photocurrent.js +4 -114
  283. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  284. package/dist/sensor/sampling-aperture.js +1 -25
  285. package/dist/sensor/sampling-aperture.js.map +1 -1
  286. package/dist/sensor/sensor-characteristics.d.ts +137 -0
  287. package/dist/sensor/sensor-characteristics.js +115 -0
  288. package/dist/sensor/sensor-characteristics.js.map +1 -0
  289. package/dist/sensor/spatial-sample-reduction.js +3 -24
  290. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  291. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  292. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  293. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  294. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  295. package/dist/sensor/spectral-coverage.d.ts +10 -1
  296. package/dist/sensor/spectral-coverage.js +1 -1
  297. package/dist/sensor/spectral-coverage.js.map +1 -1
  298. package/dist/sensor/spectral-quadrature.js +3 -41
  299. package/dist/sensor/spectral-quadrature.js.map +1 -1
  300. package/dist/sensor/spectral-response.js +1 -47
  301. package/dist/sensor/spectral-response.js.map +1 -1
  302. package/dist/sensor/temporal-photo-signal.d.ts +3 -0
  303. package/dist/sensor/temporal-photo-signal.js +5 -2
  304. package/dist/sensor/temporal-photo-signal.js.map +1 -1
  305. package/dist/stabilization/camera-shake.js +1 -6
  306. package/dist/stabilization/camera-shake.js.map +1 -1
  307. package/dist/stabilization/system.d.ts +9 -0
  308. package/dist/stabilization/system.js +7 -86
  309. package/dist/stabilization/system.js.map +1 -1
  310. package/docs/AGENT_GUIDE.md +5 -0
  311. package/docs/API_REFERENCE.md +116 -3
  312. package/docs/QUICK_START.md +178 -14
  313. package/docs/USAGE.md +111 -3
  314. package/docs/api/capture-native-raw-dng.md +6 -0
  315. package/docs/api/core-version.md +1 -1
  316. package/docs/api/equipment-generic-lens-catalogue.md +7 -2
  317. package/docs/api/exports.json +203 -3
  318. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  319. package/docs/api/facade-develop-joined-parts.md +32 -0
  320. package/docs/api/facade-display-view-gpu.md +5 -2
  321. package/docs/api/facade-fast-methods.md +91 -0
  322. package/docs/api/facade-simulate-capture.md +72 -16
  323. package/docs/api/facade-simulate-display-view.md +3 -2
  324. package/docs/api/optics-aperture-diameter.md +51 -0
  325. package/docs/api/optics-detail-limit.md +203 -0
  326. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  327. package/docs/api/optics-lens-psf-profile.md +3 -1
  328. package/docs/api/output-crop-to-match-focal-length.md +67 -0
  329. package/docs/api/output-developed-lens-corrections.md +12 -0
  330. package/docs/api/output-generic-lens-corrections.md +3 -10
  331. package/docs/api/output-lens-corrections.md +11 -1
  332. package/docs/api/output-print-size-blur-noise.md +115 -0
  333. package/docs/api/sensor-sensor-characteristics.md +105 -0
  334. package/llms.txt +5 -0
  335. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ # exposure/filtered-exposure-time.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
+ ## calculateFilteredExposureTime
6
+
7
+ Shutter time that keeps the same exposure after adding front-of-lens filters, such as a
8
+ neutral-density (ND) filter: t / T, with T the combined transmittance of the filter profiles.
9
+ Aperture and ISO are unchanged.
10
+
11
+ Exact under the linear sensor model (exposure H = E·t); film reciprocity failure is not modelled.
12
+ Nominal filters vary: a "10-stop" or ND1000 (OD 3.0, 9.97 stops) filter transmits roughly, not
13
+ exactly, its rating, so the result is only as good as the profile. The envelope is an
14
+ approximation whenever any profile's `scientificStatus` is `"approximation"`.
15
+
16
+ Example: 1/125 s with `GENERIC_ND_FILTER_PROFILES[10]` (exactly 2^-10) gives 1024/125 = 8.192 s;
17
+ on a third-stop dial, 8 s.
18
+
19
+ ```ts
20
+ export function calculateFilteredExposureTime(
21
+ input: CalculateFilteredExposureTimeInput
22
+ ): CalculationResult<FilteredExposureTime>;
23
+ ```
24
+
25
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
26
+
27
+ ## CalculateFilteredExposureTimeInput
28
+
29
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
30
+
31
+ ```ts
32
+ export interface CalculateFilteredExposureTimeInput {
33
+ /** Shutter time without the filter, in seconds. */
34
+ shutterSeconds: number;
35
+ /**
36
+ * One front-of-lens filter profile, or several in light-path order (scene side first), as
37
+ * accepted by `composeFrontOfLensFilterTransmission`.
38
+ */
39
+ filter: FrontOfLensFilterProfile | readonly FrontOfLensFilterProfile[];
40
+ /**
41
+ * Optional shutter dial, in seconds (for example `GENERIC_THIRD_STOP_SHUTTER_SECONDS`). When
42
+ * given, the result also reports the nearest dial value (nearest in stops, the shorter on a tie).
43
+ */
44
+ shutterDialSeconds?: readonly number[];
45
+ /** Wavelength at which the filters are resolved. Defaults to 550 nm. Neutral filters do not depend on it. */
46
+ wavelengthNanometers?: number;
47
+ /** Wavelength basis; defaults to the first filter profile's basis and must match every profile. */
48
+ wavelengthBasis?: Exclude<SpectralWavelengthBasis, "unspecified">;
49
+ /** Incident polarization, which matters only when a polarizer is in the stack. Omitted means unpolarized. */
50
+ incidentPolarization?: LinearPolarizationState;
51
+ }
52
+ ```
53
+
54
+ ## FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS
55
+
56
+ Wavelength used when the caller gives none: 550 nm, near the photopic peak. Neutral filters do not depend on it.
57
+
58
+ ```ts
59
+ FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS = 550 as const
60
+ ```
61
+
62
+ ## FilteredExposureDialSetting
63
+
64
+ The dial value nearest the exact filtered shutter time.
65
+
66
+ ```ts
67
+ export interface FilteredExposureDialSetting {
68
+ /** Nearest dial value in seconds. */
69
+ shutterSeconds: number;
70
+ /**
71
+ * log2(dial / exact filtered time): exposure of the dial setting relative to the unfiltered
72
+ * exposure, in stops. Positive is brighter (longer), negative darker.
73
+ */
74
+ exposureErrorStops: number;
75
+ /** `"maximum"` when the exact time is longer than every dial value (use bulb for the exact time), `"minimum"` when shorter than every one. */
76
+ clamped: false | "minimum" | "maximum";
77
+ /** How the dial value was chosen. */
78
+ quantizationPolicy: "nearest-log2-shorter-on-tie";
79
+ }
80
+ ```
81
+
82
+ ## FilteredExposureTime
83
+
84
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
85
+
86
+ ```ts
87
+ export interface FilteredExposureTime {
88
+ /** Shutter time without the filter, in seconds. */
89
+ shutterSeconds: number;
90
+ /** Shutter time that gives the same exposure with the filter: shutterSeconds / linearTransmissionFactor. */
91
+ filteredShutterSeconds: number;
92
+ /** Combined intensity transmittance T of the filter stack, in (0, 1]. */
93
+ linearTransmissionFactor: number;
94
+ /** Combined attenuation −log2 T, in stops. */
95
+ attenuationStops: number;
96
+ /** Shutter multiplier 1/T = 2^attenuationStops. */
97
+ exposureFactor: number;
98
+ /** Wavelength at which the filters were resolved, in nanometres. */
99
+ wavelengthNanometers: number;
100
+ /** The filter-stack transmission this result is based on, with each filter's identity and evidence. */
101
+ filterTransmission: ComposedFrontOfLensFilterTransmission;
102
+ /** Nearest shutter-dial value; present only when `shutterDialSeconds` was given. */
103
+ dial?: FilteredExposureDialSetting;
104
+ }
105
+ ```
@@ -0,0 +1,32 @@
1
+ # facade/develop-joined-parts.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
+ ## simulateCaptureFromParts
6
+
7
+ Develops the whole frame of `options` from the packed RAW of region renders of the same request (#571), so a capture can
8
+ render its RAW in parallel parts and still take the development steps that filter across the whole image:
9
+ `output.diffractionCorrection` and `output.sharpening`, which a region render refuses, and `output.lensCorrections` (#570), which
10
+ runs on the whole developed frame here exactly as in `simulateCapture(options)`. The parts are joined exactly as
11
+ {@link serializeSimulatedCaptureDngFromParts } joins them (they must cover every native site and agree where they overlap),
12
+ and the joined RAW is the whole render's RAW, so the result's `image` (samples and JPEG or PNG bytes), `raw` codes and
13
+ `plan` are those of `simulateCapture(options)`, bit for bit.
14
+
15
+ `options` is the whole request (no `output.region`), with the development options it should have; render the parts with
16
+ the same request plus `output.region`, without `output.diffractionCorrection`, `output.sharpening` and `output.dng`, and with a
17
+ fixed white balance in place of `"auto"` (none of these change the RAW codes; auto white balance is measured here over the
18
+ joined frame). A part may keep or drop `output.lensCorrections` (a corrected region only widens its `region.nativeRect`).
19
+ Any `output.format` serves for a part.
20
+
21
+ `onProgress` reports the `white-balance` (with auto white balance), `development`, `diffraction-correction`, `sharpening` and
22
+ `jpeg` stages, the detail stages once per output row, so a host can show "Applying sharpening to the whole image". The lens
23
+ corrections report no stage of their own: they run between `diffraction-correction` and `sharpening`.
24
+ No `raw` stage is reported: no RAW is rendered. What differs from the whole render's result: `diagnostics.providerEvaluationCount`
25
+ is 0, `diagnostics.timingsMs` are this call's, `diagnostics.fast` and the `raw.*` assumptions of how the RAW was rendered are
26
+ not repeated (they are in the parts' results), and a `parts` assumption records the join.
27
+
28
+ ```ts
29
+ export async function simulateCaptureFromParts(options: SimulateCaptureOptions, parts: readonly SimulateCaptureDngPart[]): Promise<SimulateCaptureResult>;
30
+ ```
31
+
32
+ This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
@@ -81,13 +81,16 @@ export type DisplayViewGpuMode = "off" | "auto" | "require";
81
81
 
82
82
  ```ts
83
83
  export interface PrepareDisplayViewOptions {
84
- /** See {@link DisplayViewGpuMode}. Default `"off"`. */
84
+ /**
85
+ * See {@link DisplayViewGpuMode}. 2.20.0: default `"auto"` when the request resolves to fast (`fast` omitted or true: the
86
+ * display view's default) and `"off"` when it is exact (`fast: false`); an explicit value wins.
87
+ */
85
88
  gpu?: DisplayViewGpuMode;
86
89
  /**
87
90
  * Where the device comes from, with `gpu: "auto"` or `"require"`: a WebGPU entry point (a worker's `navigator.gpu`), or an
88
91
  * `ImageKernelAccelerator` whose device the view shares (the accelerator keeps ownership; `dispose()` of the prepared view
89
92
  * leaves it alone). Without it the engine acquires a device from `globalThis.navigator.gpu` and owns it until `dispose()`.
90
- * Refused with `gpu: "off"`.
93
+ * Refused when `gpu` is or defaults to `"off"`.
91
94
  */
92
95
  gpuDevice?: unknown;
93
96
  }
@@ -0,0 +1,91 @@
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, 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: "output.demosaic.ahd/view-low-signal-correction", override: "output.demosaic", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
28
+ fast: "(H3) 2048 draws per correction state, applied where a pixel's linear noise exceeds 0.01",
29
+ exact: "8192 draws per state, at the same 0.01 threshold", followsFormatDefault: true,
30
+ resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd", assumption: "view.demosaic" } },
31
+ { id: "view.uniform-pixel-sampling", override: "fast", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
32
+ fast: "(H2) a quarter of the samples per axis in display pixels whose pilot finds no detail",
33
+ exact: "the full samples per axis in every display pixel", followsFormatDefault: true,
34
+ resolver: { file: CAPTURE, call: "uniformSampling: fastTop" }, report: { assumption: "view.estimation" } },
35
+ { id: "prepareDisplayView.gpu", override: "fast", methods: "fast-and-exact", runs: ["prepareDisplayView"],
36
+ fast: "gpu \"auto\": WebGPU float32 where a conforming device supports the request (a bounded approximation), else the CPU reference",
37
+ exact: "gpu \"off\": the CPU reference; an explicit gpu wins", followsFormatDefault: true,
38
+ resolver: { file: VIEW, call: "parsePrepareDisplayViewOptions(prepareOptions, (options as { fast?: unknown } | null | undefined)?.fast !== false)" },
39
+ report: { field: "diagnostics.fast.execution" } },
40
+ { id: "sensor.midGrey", override: "fast", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
41
+ fast: "#535: the mid-gray noise figure is not computed (about 0.3 ms)", exact: "result.sensor.midGrey is computed", followsFormatDefault: true,
42
+ resolver: { file: CAPTURE, call: "fastTop ? undefined : 0" }, report: { field: "sensor.midGrey" } },
43
+ { id: "lens.diffraction", override: "lens.diffraction", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
44
+ fast: "", exact: "the diffraction kernels (a fast method only under rolling-shutter rotation, above)", followsFormatDefault: true,
45
+ resolver: { file: CAPTURE, call: "resolveFast(diffractionFast, fastTop)" }, report: { applied: "lens.diffraction" } },
46
+ { id: "sensor.antiAliasing", override: "sensor.antiAliasing", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
47
+ fast: "", exact: "the point-splitting filter", followsFormatDefault: true,
48
+ resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.antiAliasing" } },
49
+ { id: "sensor.pixelAperture.box", override: "sensor.pixelAperture", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
50
+ fast: "", exact: "the N × N midpoint rule", followsFormatDefault: true,
51
+ resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.pixelAperture.box" } },
52
+ { id: "output.demosaic.ahd", override: "output.demosaic", methods: "exact-only", runs: ["simulateCapture"],
53
+ fast: "", exact: "the AHD demosaic of the capture (a fast method only in the display view's correction, above)", followsFormatDefault: true,
54
+ resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd" } }
55
+ ] as const satisfies readonly SimulateCaptureFastMethod[])
56
+ ```
57
+
58
+ ## SimulateCaptureFastMethod
59
+
60
+ One speed-versus-accuracy choice of {@link SIMULATE_CAPTURE_FAST_METHODS} (2.20.0).
61
+
62
+ ```ts
63
+ export interface SimulateCaptureFastMethod {
64
+ /** A short id: the option path and, where one option has several fast methods, the method. */
65
+ id: string;
66
+ /**
67
+ * The request path whose record takes the `fast` override (`allowKeys` path in simulate-capture.ts), or "fast" when only the
68
+ * top-level flag sets it (a choice with no option record of its own).
69
+ */
70
+ override: string;
71
+ /** "fast-and-exact": both methods exist; "exact-only": the record takes `fast`, but there is no fast method yet (reports false). */
72
+ methods: "fast-and-exact" | "exact-only";
73
+ /** Where it applies. */
74
+ runs: readonly ("simulateCapture" | "simulateDisplayView" | "prepareDisplayView")[];
75
+ /** The fast method and the exact one, in words (empty for exact-only). */
76
+ fast: string;
77
+ exact: string;
78
+ /** Always true: no choice keeps its own default (the test checks it). */
79
+ followsFormatDefault: true;
80
+ /**
81
+ * The resolver call in the source (`resolveFast(<record flag>, fastTop)`, or `fastTop` itself for a top-level choice), which the
82
+ * test finds in the named file.
83
+ */
84
+ resolver: { file: string; call: string };
85
+ /**
86
+ * How the result reports it: the `diagnostics.fast.applied` key (true where the fast method ran), an assumption id the fast
87
+ * method adds, or another result field. At least one; the test finds each assumption id in the source.
88
+ */
89
+ report: { applied?: string; assumption?: string; field?: string };
90
+ }
91
+ ```
@@ -66,6 +66,8 @@ export interface DisplayViewResult {
66
66
  /** #516: as in the capture result. */
67
67
  isoControl: "manual" | "automatic"; autoIso?: SimulateCaptureAutoIsoReport };
68
68
  scene: SimulateCaptureResult["scene"];
69
+ /** #555, #535: as in the capture result. */
70
+ sensor: SimulateCaptureSensorReport;
69
71
  /**
70
72
  * Work done: real pixels sampled, real-site expectations evaluated, scene queries, display pixels sampled in full (detail) or with
71
73
  * a quarter of the samples per axis (uniform), display pixels whose value includes the AHD low-signal correction, and wall-clock
@@ -144,7 +146,7 @@ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.tempor
144
146
  SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
145
147
  "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
146
148
  "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
147
- "output.diffractionCorrection", "output.sharpening", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
149
+ "output.diffractionCorrection", "output.sharpening", "output.lensCorrections", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
148
150
  ```
149
151
 
150
152
  ## SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX
@@ -425,6 +427,12 @@ One part of a whole-frame DNG (#391): a region render's packed RAW and where its
425
427
  export interface SimulateCaptureDngPart {
426
428
  raw: Pick<NativeRawOutput, "codes" | "blackLevels" | "digitalSaturationCodes" | "saturationFlags" | "effectiveSaturationCodes">;
427
429
  region: { nativeRect: { x: number; y: number; width: number; height: number } };
430
+ /**
431
+ * 2.20.0: true when a fast method ran for this part (`Object.values(result.diagnostics.fast.applied).includes(true)`). The join
432
+ * refuses such a part unless its own request resolves to fast too (a DNG, JPEG or PNG is exact by default): render the parts with
433
+ * the same `fast` as the join, or with `fast: false`.
434
+ */
435
+ fast?: boolean;
428
436
  }
429
437
  ```
430
438
 
@@ -450,8 +458,10 @@ export type SimulateCaptureFastOption = typeof SIMULATE_CAPTURE_FAST_OPTIONS[num
450
458
 
451
459
  ```ts
452
460
  export interface SimulateCaptureFastReport {
453
- /** True when `fast: true` was given at the top level or in any option's record. */
461
+ /** 2.20.0: the resolved top-level value (`fast`, else the format default), or true when any option's record sets `fast: true`. */
454
462
  requested: boolean;
463
+ /** 2.20.0: where the top-level value came from: the request's `fast`, or the format default (see {@link SimulateCaptureOptions.fast}). */
464
+ defaultedFrom: "request" | "format";
455
465
  /** Each fast-capable option that ran, in {@link SIMULATE_CAPTURE_FAST_OPTIONS} order: true where its fast method ran. */
456
466
  applied: Readonly<Partial<Record<SimulateCaptureFastOption, boolean>>>;
457
467
  /** `engine-reference` when every option ran exact; `approximation-with-a-priori-bound` when any fast method ran (the WebGPU execution record's values). */
@@ -462,11 +472,30 @@ export interface SimulateCaptureFastReport {
462
472
  ## SimulateCaptureLensCatalogueRecord
463
473
 
464
474
  `lens.catalogue` as a record (#568): the lens id as `kind` and the `fast` override (see {@link SimulateCaptureOptions.fast}).
475
+ Fast: the tabulated fast method (#568), and with `lens.defocus` the lens's bokeh without its cat's eye (#567; fast wides and
476
+ normals only), whose field-varying PSF needs a finer fast grid (measured 2.2–3.4× slower). Exact: the per-site PSF with the
477
+ cat's eye. Blades and spherical aberration always apply (they cost nothing).
465
478
 
466
479
  ```ts
467
480
  export interface SimulateCaptureLensCatalogueRecord { kind: string; fast?: boolean }
468
481
  ```
469
482
 
483
+ ## SimulateCaptureLensCorrections
484
+
485
+ `output.lensCorrections` (#570): which of the catalogue lens's generic corrections (`createGenericLensCorrectionProfile`) the
486
+ development applies. Each is off unless set to true.
487
+
488
+ ```ts
489
+ export interface SimulateCaptureLensCorrections {
490
+ /** Radial distortion r_d = r(1 + k1ρ²), inverted by resampling: it stretches the corners and lowers their resolution. */
491
+ distortion?: boolean;
492
+ /** Lateral colour: each channel rescaled by 1/(1 + L·D(λ_c)) into register; axial (longitudinal) colour stays. */
493
+ lateralColor?: boolean;
494
+ /** Vignetting: a gain 1/E(ρ) at each pixel's source point; corner noise rises with the signal, the SNR does not. */
495
+ vignetting?: boolean;
496
+ }
497
+ ```
498
+
470
499
  ## SimulateCaptureLensDistortion
471
500
 
472
501
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -531,7 +560,8 @@ export interface SimulateCaptureOptions {
531
560
  * distance beyond the focal length (no minimum focus distance; `subjectDistanceM` must be at least twice the focal length, as for
532
561
  * every lens). Works in `simulateCapture` and the display view. Not combinable with `prime`, `catalogue` or `focalLengthMm`.
533
562
  * Optional `widestFNumber` and `narrowestFNumber` (within `IDEAL_LENS_LIMITS`, widest ≤ narrowest; each defaults to its bound)
534
- * narrow the range: `fNumber` must lie in it, and shutter priority and program choose apertures only in it. Default off.
563
+ * narrow the range: `fNumber` must lie in it, and shutter priority and program choose apertures only in it (program limits its
564
+ * line to a range that does not hold f/4–f/16, #583). Default off.
535
565
  */
536
566
  ideal?: { focalLengthMm: number; widestFNumber?: number; narrowestFNumber?: number };
537
567
  /**
@@ -540,9 +570,9 @@ export interface SimulateCaptureOptions {
540
570
  * factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
541
571
  * lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
542
572
  * colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
543
- * and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
573
+ * and, for the fisheyes, the equisolid projection. Flare and focus breathing are not rendered. Not combinable
544
574
  * with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
545
- * lens sets them); `defocus` convolves the disc into the lens's PSF. The display view renders it with the PSF taken per field zone (#569; docs/DISPLAY_VIEW.md). Default off.
575
+ * lens sets them); `defocus` convolves the lens's bokeh kernel (#567) into its PSF. The display view renders it with the PSF taken per field zone (#569; docs/DISPLAY_VIEW.md). Default off.
546
576
  * #568: the record form `{ kind: <lens id>, fast }` overrides the top-level `fast` flag for this option; the fast method
547
577
  * blends the PSF between exactly resolved kernels on a field grid and tabulates each site's expected signal
548
578
  * (docs/LENS_CATALOGUE.md, "Fast method").
@@ -683,7 +713,7 @@ export interface SimulateCaptureOptions {
683
713
  exposureCompensationStops?: number;
684
714
  /** Default "scene-illuminant". */
685
715
  whiteBalance?: SimulateCaptureWhiteBalance;
686
- /** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 2. */
716
+ /** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 1 (2.20.0; 2 before), the full-quality export. */
687
717
  jpegQuantizationStep?: number;
688
718
  /** PNG only. Default "srgb". */
689
719
  colorEncoding?: SimulateCaptureColorEncoding;
@@ -698,8 +728,13 @@ export interface SimulateCaptureOptions {
698
728
  demosaic?: SimulateCaptureDemosaic | SimulateCaptureDemosaicRecord;
699
729
  /** Default "margin". */
700
730
  borderHandling?: SimulateCaptureBorderHandling;
701
- /** DNG only: optional capture-event time (YYYY-MM-DDTHH:mm:ss.sssZ); without it no date tags are written. */
702
- dng?: { capturedAtUtc?: string };
731
+ /**
732
+ * DNG only: optional capture-event time (YYYY-MM-DDTHH:mm:ss.sssZ); without it no date tags are written. Opt-in (#587), with
733
+ * `lens.catalogue`: `lensCorrectionOpcodes` writes the selected corrections of {@link SimulateCaptureLensCorrections} into the DNG as
734
+ * optional DNG opcodes in OpcodeList3 (FixVignetteRadial, WarpRectilinear with the zoom-to-fill; docs/NATIVE_RAW_AND_PRINT.md), for RAW editors
735
+ * that apply them; the RAW codes are unchanged. Whole frame only (no `region`). All off (or omitted): the same DNG bytes as without it.
736
+ */
737
+ dng?: { capturedAtUtc?: string; lensCorrectionOpcodes?: SimulateCaptureLensCorrections };
703
738
  /**
704
739
  * Opt-in (2.4) text written into the file for formats "jpeg", "png" and "dng": the caller's description,
705
740
  * software, copyright and comment, unchanged (JPEG XMP + COM, PNG tEXt/iTXt, DNG TIFF tags + XMP; see the
@@ -738,10 +773,21 @@ export interface SimulateCaptureOptions {
738
773
  * (not the wide-gamut or perceptual PNG paths); not combinable with `region`. The DNG's RAW codes are not changed. Default off.
739
774
  */
740
775
  sharpening?: SimulateCaptureSharpening;
776
+ /**
777
+ * Opt-in (#570), with `lens.catalogue`: the lens's generic corrections (see {@link SimulateCaptureLensCorrections}) on the
778
+ * developed linear colour, after demosaic, colour, white balance and `diffractionCorrection`, before the tone curve; for the JPEG,
779
+ * PNG and samples. The RAW and the DNG are never corrected. To keep the output size with no missing pixels, the corrected image is
780
+ * magnified by the smallest factor 1/m ≥ 1 that keeps every pixel's source inside the frame (recorded in the
781
+ * `output.lensCorrections` assumption). Works with `region`: a region develops the frame pixels its corrected pixels read
782
+ * (`region.nativeRect`, `raw` and `plan` describe that rectangle) and its pixels are the whole render's. All off (or omitted):
783
+ * the development is unchanged.
784
+ */
785
+ lensCorrections?: SimulateCaptureLensCorrections;
741
786
  };
742
787
  /** Camera motion and shutter type. Default: still camera, global electronic shutter. */
743
788
  motion?: SimulateCaptureMotion;
744
- sensor?: {
789
+ /** #555: plus the sensor characteristics ({@link SimulateCaptureSensorCharacteristics}), each off by default. */
790
+ sensor?: SimulateCaptureSensorCharacteristics & {
745
791
  /**
746
792
  * Pixel pitch in micrometres, 0.5–1000. Default 6. The simulated sensor is output width × pitch wide. 2.5: above 100 µm the
747
793
  * capture is a downsized view of a whole frame (for example 36 mm / 48 pixels = 750 µm), recorded in the assumption
@@ -768,15 +814,19 @@ export interface SimulateCaptureOptions {
768
814
  pixelAperture?: SimulateCapturePixelAperture;
769
815
  };
770
816
  /**
771
- * Exact or fast (#395). `false` or omitted: every option runs its exact method, the engine reference. `true`: each option
772
- * listed in {@link SIMULATE_CAPTURE_FAST_OPTIONS} runs its fast method where it has one, a measured, bounded approximation of
773
- * the exact one; an option without a fast method runs exact. An option's own record `fast` overrides this for that option
774
- * only. `diagnostics.fast` reports what was requested and what ran. `lens.catalogue` has a fast method (#568); the other options run exact.
817
+ * Exact or fast (#395). `false`: every option runs its exact method, the engine reference. `true`: each option listed in
818
+ * {@link SIMULATE_CAPTURE_FAST_OPTIONS} runs its fast method where it has one, a measured, bounded approximation of the exact
819
+ * one; an option without a fast method runs exact. An option's own record `fast` overrides this for that option only.
820
+ * Omitted (2.20.0), the format decides: fast for `output.format` "srgb-8" and "srgb-16" and for the display view, exact for
821
+ * "dng", "jpeg" and "png" (exports) and for `simulateIdealReference`. `diagnostics.fast` reports the resolved value, where it
822
+ * came from and what ran. Fast methods: `lens.catalogue` (#568, #567) and `lens.diffraction` under rolling-shutter rotation
823
+ * (#565); in the display view also its catalogue PSF zones, its sampling of uniform pixels and its AHD low-signal correction
824
+ * (docs/DISPLAY_VIEW.md). The exact result also carries `result.sensor.midGrey` (#535). Calculators should pass `fast: false`.
775
825
  */
776
826
  fast?: boolean;
777
827
  /** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
778
828
  signal?: AbortSignal;
779
- /** Called after each committed RAW, development or JPEG tile. Cannot change output. */
829
+ /** Called after each committed RAW, development, detail or JPEG tile (see {@link SimulateCaptureProgress}). Cannot change output. */
780
830
  onProgress?: (progress: SimulateCaptureProgress) => void;
781
831
  }
782
832
  ```
@@ -799,11 +849,15 @@ export type SimulateCapturePixelAperture = "point" | "box" | { kind: "box"; samp
799
849
 
800
850
  ## SimulateCaptureProgress
801
851
 
802
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
852
+ One progress event of `onProgress`. Stages run in this order: `raw`, `white-balance` (with auto white balance), `development`
853
+ (rows), then, when requested, `diffraction-correction` and `sharpening` (#571: one event per output row of the whole image;
854
+ each stage's last event has `completedTileCount === totalTileCount`), then `jpeg`. `output.lensCorrections` (#570) runs between
855
+ `diffraction-correction` and `sharpening` and reports no stage of its own. {@link simulateCaptureFromParts }
856
+ reports no `raw` stage: its RAW comes from the parts.
803
857
 
804
858
  ```ts
805
859
  export interface SimulateCaptureProgress {
806
- stage: "raw" | "white-balance" | "development" | "jpeg";
860
+ stage: "raw" | "white-balance" | "development" | "diffraction-correction" | "sharpening" | "jpeg";
807
861
  completedTileCount: number;
808
862
  totalTileCount: number;
809
863
  }
@@ -963,6 +1017,8 @@ export interface SimulateCaptureResult {
963
1017
  limitations: readonly string[];
964
1018
  };
965
1019
  assumptions: readonly SimulateCaptureAssumption[];
1020
+ /** #555, #535: the sensor characteristics used, the calculated dynamic range and (when the request resolves to exact) the mid-grey noise figure. */
1021
+ sensor: SimulateCaptureSensorReport;
966
1022
  }
967
1023
  ```
968
1024
 
@@ -65,9 +65,10 @@ responses through the environment chain, the diffraction kernels, readout moment
65
65
  about 0.3–1.3 s per call on a 24 MP request; a region costs only its own pixels. Errors in the request are raised here;
66
66
  errors in the view by `render`.
67
67
 
68
- 2.11 (#481): `prepareOptions.gpu` chooses the renderer: `"off"` (default) is the CPU reference, today's bytes; `"auto"` renders
68
+ 2.11 (#481): `prepareOptions.gpu` chooses the renderer: `"off"` is the CPU reference, today's bytes; `"auto"` renders
69
69
  on WebGPU when a conforming device is available and the request is supported, else on the CPU, labelled; `"require"` fails
70
- instead of falling back (`ImageKernelExecutionError`, acquisition failures here, later ones from `render`). `gpuDevice` is a
70
+ instead of falling back (`ImageKernelExecutionError`, acquisition failures here, later ones from `render`). 2.20.0: the default is
71
+ `"auto"` when the request resolves to fast (the view's default) and `"off"` with `fast: false`. `gpuDevice` is a
71
72
  WebGPU entry point or an `ImageKernelAccelerator` whose device the view shares; without it the engine acquires one device
72
73
  for the prepared view (the request's `signal` aborts the acquisition) and `dispose()` releases it. The realm's
73
74
  `setWebGpuEnabled(false)` vetoes both GPU modes (`webgpu-disabled`). See docs/DISPLAY_VIEW.md, "Rendering on the GPU".
@@ -0,0 +1,51 @@
1
+ # optics/aperture-diameter.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
+ ## ApertureDiameter
6
+
7
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
8
+
9
+ ```ts
10
+ export interface ApertureDiameter {
11
+ /** Focal length supplied by the caller, in millimetres. */
12
+ focalLengthMm: number;
13
+ /** f-number supplied by the caller. */
14
+ fNumber: number;
15
+ /** Entrance-pupil diameter D = focalLengthMm / fNumber, in millimetres. */
16
+ entrancePupilDiameterMm: number;
17
+ }
18
+ ```
19
+
20
+ ## calculateApertureDiameter
21
+
22
+ Entrance-pupil (aperture) diameter in millimetres: focal length ÷ f-number.
23
+
24
+ Exact by the definition of the f-number for an ideal lens focused at infinity. It is the
25
+ entrance pupil (the aperture stop as seen from the front), not the diaphragm's physical
26
+ opening. Close focus (working f-number N·(1 + m/P) with pupil magnification P) and focal-length
27
+ changes while focusing are not modelled.
28
+
29
+ Example: 50 mm at f/2 gives 25 mm; at f/4, 12.5 mm (half the diameter, a quarter of the area:
30
+ two stops).
31
+
32
+ ```ts
33
+ export function calculateApertureDiameter(
34
+ input: CalculateApertureDiameterInput
35
+ ): CalculationResult<ApertureDiameter>;
36
+ ```
37
+
38
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
39
+
40
+ ## CalculateApertureDiameterInput
41
+
42
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
43
+
44
+ ```ts
45
+ export interface CalculateApertureDiameterInput {
46
+ /** Focal length of the lens in millimetres (the physical focal length, not a 35 mm equivalent). */
47
+ focalLengthMm: number;
48
+ /** f-number set on the lens (N = focal length / entrance-pupil diameter). */
49
+ fNumber: number;
50
+ }
51
+ ```