@photivra/engine 2.18.0 → 2.19.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 (261) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/capture/focus-control.js +2 -75
  6. package/dist/capture/focus-control.js.map +1 -1
  7. package/dist/capture/native-environment-lean.d.ts +1 -0
  8. package/dist/capture/native-environment-lean.js +1 -1
  9. package/dist/capture/native-environment-lean.js.map +1 -1
  10. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  11. package/dist/capture/native-environment-optical-stage.js +2 -5
  12. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  13. package/dist/capture/native-environment-raw.js +1 -5
  14. package/dist/capture/native-environment-raw.js.map +1 -1
  15. package/dist/capture/release-sequence.js +2 -47
  16. package/dist/capture/release-sequence.js.map +1 -1
  17. package/dist/color/white-balance.d.ts +2 -0
  18. package/dist/color/white-balance.js +3 -56
  19. package/dist/color/white-balance.js.map +1 -1
  20. package/dist/composition/image-formation-plan.d.ts +1 -0
  21. package/dist/composition/image-formation-plan.js +3 -49
  22. package/dist/composition/image-formation-plan.js.map +1 -1
  23. package/dist/core/evidence-provenance.js +1 -16
  24. package/dist/core/evidence-provenance.js.map +1 -1
  25. package/dist/core/record-validation.d.ts +54 -0
  26. package/dist/core/record-validation.js +176 -0
  27. package/dist/core/record-validation.js.map +1 -1
  28. package/dist/core/scientific-assurance.js +1 -18
  29. package/dist/core/scientific-assurance.js.map +1 -1
  30. package/dist/core/shared-helpers.d.ts +33 -0
  31. package/dist/core/shared-helpers.js +34 -0
  32. package/dist/core/shared-helpers.js.map +1 -0
  33. package/dist/core/spectral-composition.js +1 -36
  34. package/dist/core/spectral-composition.js.map +1 -1
  35. package/dist/core/spectral.js +1 -19
  36. package/dist/core/spectral.js.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/core/version.js.map +1 -1
  40. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  41. package/dist/equipment/exposure-capabilities.js +2 -36
  42. package/dist/equipment/exposure-capabilities.js.map +1 -1
  43. package/dist/equipment/iso-capabilities.js +2 -49
  44. package/dist/equipment/iso-capabilities.js.map +1 -1
  45. package/dist/equipment/metering-capabilities.js +1 -26
  46. package/dist/equipment/metering-capabilities.js.map +1 -1
  47. package/dist/equipment/release-capabilities.js +1 -53
  48. package/dist/equipment/release-capabilities.js.map +1 -1
  49. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  50. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  51. package/dist/exposure/exposure-duration-control.js +1 -16
  52. package/dist/exposure/exposure-duration-control.js.map +1 -1
  53. package/dist/exposure/exposure-mode-resolver.js +1 -17
  54. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  55. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  56. package/dist/exposure/filtered-exposure-time.js +129 -0
  57. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  58. package/dist/exposure/flash-ttl.d.ts +1 -0
  59. package/dist/exposure/flash-ttl.js +1 -1
  60. package/dist/exposure/flash-ttl.js.map +1 -1
  61. package/dist/exposure/flash.js +1 -49
  62. package/dist/exposure/flash.js.map +1 -1
  63. package/dist/exposure/metering-scene-radiance.js +1 -25
  64. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  65. package/dist/exposure/metering-target.js +1 -30
  66. package/dist/exposure/metering-target.js.map +1 -1
  67. package/dist/exposure/metering-temporal.js +1 -8
  68. package/dist/exposure/metering-temporal.js.map +1 -1
  69. package/dist/exposure/metering.js +1 -72
  70. package/dist/exposure/metering.js.map +1 -1
  71. package/dist/exposure/program-line.js +1 -45
  72. package/dist/exposure/program-line.js.map +1 -1
  73. package/dist/facade/develop-joined-parts.d.ts +29 -0
  74. package/dist/facade/develop-joined-parts.js +33 -0
  75. package/dist/facade/develop-joined-parts.js.map +1 -0
  76. package/dist/facade/generic-lens-capture.d.ts +4 -2
  77. package/dist/facade/generic-lens-capture.js +4 -2
  78. package/dist/facade/generic-lens-capture.js.map +1 -1
  79. package/dist/facade/simulate-capture.d.ts +37 -5
  80. package/dist/facade/simulate-capture.js +153 -96
  81. package/dist/facade/simulate-capture.js.map +1 -1
  82. package/dist/gpu/chart-composite-gpu.js +1 -3
  83. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  84. package/dist/gpu/chart-radiance-gpu.js +1 -5
  85. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  86. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  87. package/dist/gpu/display-view-quality-gpu.js +4 -8
  88. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  89. package/dist/gpu/display-view-speed-gpu.js +3 -25
  90. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  91. package/dist/gpu/sensor-stage-gpu.js +1 -5
  92. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  93. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  94. package/dist/gpu/webgpu-runtime.js +3 -0
  95. package/dist/gpu/webgpu-runtime.js.map +1 -1
  96. package/dist/index.d.ts +4 -1
  97. package/dist/index.js +3 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/motion/camera-rotation.js +1 -6
  100. package/dist/motion/camera-rotation.js.map +1 -1
  101. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  102. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  103. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  104. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  105. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  106. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  107. package/dist/motion/capture-rotation-trajectory.js +2 -17
  108. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  109. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  110. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  111. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  112. package/dist/motion/extended-object-projection.js +5 -40
  113. package/dist/motion/extended-object-projection.js.map +1 -1
  114. package/dist/optics/atmospheric-haze.js +1 -7
  115. package/dist/optics/atmospheric-haze.js.map +1 -1
  116. package/dist/optics/brown-conrady-distortion.js +1 -7
  117. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  118. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  119. package/dist/optics/complex-pupil-psf.js +2 -84
  120. package/dist/optics/complex-pupil-psf.js.map +1 -1
  121. package/dist/optics/focus-state.js +1 -11
  122. package/dist/optics/focus-state.js.map +1 -1
  123. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  124. package/dist/optics/front-of-lens-filter.js +3 -65
  125. package/dist/optics/front-of-lens-filter.js.map +1 -1
  126. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  127. package/dist/optics/generic-nd-filter-presets.js +48 -0
  128. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  129. package/dist/optics/illumination-vignetting.js +1 -6
  130. package/dist/optics/illumination-vignetting.js.map +1 -1
  131. package/dist/optics/lens-psf-profile.d.ts +4 -1
  132. package/dist/optics/lens-psf-profile.js +12 -111
  133. package/dist/optics/lens-psf-profile.js.map +1 -1
  134. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  135. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  136. package/dist/optics/pupil-vignetting.js +1 -7
  137. package/dist/optics/pupil-vignetting.js.map +1 -1
  138. package/dist/optics/radial-distortion.js +1 -6
  139. package/dist/optics/radial-distortion.js.map +1 -1
  140. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  141. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  142. package/dist/optics/stray-light.d.ts +1 -0
  143. package/dist/optics/stray-light.js +1 -1
  144. package/dist/optics/stray-light.js.map +1 -1
  145. package/dist/optics/zernike-wavefront.js +1 -7
  146. package/dist/optics/zernike-wavefront.js.map +1 -1
  147. package/dist/output/capture-geometry.js +1 -8
  148. package/dist/output/capture-geometry.js.map +1 -1
  149. package/dist/output/developed-lens-corrections.d.ts +62 -0
  150. package/dist/output/developed-lens-corrections.js +135 -0
  151. package/dist/output/developed-lens-corrections.js.map +1 -0
  152. package/dist/output/generic-lens-corrections.d.ts +2 -6
  153. package/dist/output/generic-lens-corrections.js +3 -6
  154. package/dist/output/generic-lens-corrections.js.map +1 -1
  155. package/dist/output/geometric-transforms.js +1 -4
  156. package/dist/output/geometric-transforms.js.map +1 -1
  157. package/dist/output/native-capture-sdr.d.ts +2 -2
  158. package/dist/output/native-capture-sdr.js +3 -2
  159. package/dist/output/native-capture-sdr.js.map +1 -1
  160. package/dist/processing/development-detail.d.ts +29 -0
  161. package/dist/processing/development-detail.js +128 -107
  162. package/dist/processing/development-detail.js.map +1 -1
  163. package/dist/schema/illumination-temporal.js +3 -72
  164. package/dist/schema/illumination-temporal.js.map +1 -1
  165. package/dist/schema/illumination.d.ts +1 -0
  166. package/dist/schema/illumination.js +2 -65
  167. package/dist/schema/illumination.js.map +1 -1
  168. package/dist/schema/scene-radiance-temporal.js +2 -14
  169. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  170. package/dist/schema/scene-radiance.js +2 -60
  171. package/dist/schema/scene-radiance.js.map +1 -1
  172. package/dist/schema/validation.d.ts +2 -0
  173. package/dist/schema/validation.js +2 -17
  174. package/dist/schema/validation.js.map +1 -1
  175. package/dist/sensor/accumulated-charge.js +2 -59
  176. package/dist/sensor/accumulated-charge.js.map +1 -1
  177. package/dist/sensor/architecture.js +1 -16
  178. package/dist/sensor/architecture.js.map +1 -1
  179. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  180. package/dist/sensor/camera-saturation-capacity.js +3 -55
  181. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  182. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  183. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  184. package/dist/sensor/capture-mode-timing.js +1 -39
  185. package/dist/sensor/capture-mode-timing.js.map +1 -1
  186. package/dist/sensor/capture-mode.js +1 -23
  187. package/dist/sensor/capture-mode.js.map +1 -1
  188. package/dist/sensor/color-sampling.js +1 -26
  189. package/dist/sensor/color-sampling.js.map +1 -1
  190. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  191. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  192. package/dist/sensor/dark-current.js +2 -45
  193. package/dist/sensor/dark-current.js.map +1 -1
  194. package/dist/sensor/eqe-electron-rate.js +2 -35
  195. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  196. package/dist/sensor/exposure-window.d.ts +5 -0
  197. package/dist/sensor/exposure-window.js +7 -22
  198. package/dist/sensor/exposure-window.js.map +1 -1
  199. package/dist/sensor/iso-gain.js +1 -7
  200. package/dist/sensor/iso-gain.js.map +1 -1
  201. package/dist/sensor/iso-signal-chain.js +1 -43
  202. package/dist/sensor/iso-signal-chain.js.map +1 -1
  203. package/dist/sensor/optical-stack.js +1 -25
  204. package/dist/sensor/optical-stack.js.map +1 -1
  205. package/dist/sensor/photon-energy.js +1 -26
  206. package/dist/sensor/photon-energy.js.map +1 -1
  207. package/dist/sensor/physical-charge-capacity.js +3 -77
  208. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  209. package/dist/sensor/radiometry-readiness.js +2 -23
  210. package/dist/sensor/radiometry-readiness.js.map +1 -1
  211. package/dist/sensor/raw-readout.d.ts +3 -0
  212. package/dist/sensor/raw-readout.js +4 -66
  213. package/dist/sensor/raw-readout.js.map +1 -1
  214. package/dist/sensor/raw-reconstruction.js +2 -64
  215. package/dist/sensor/raw-reconstruction.js.map +1 -1
  216. package/dist/sensor/readout-exposure-linkage.js +1 -9
  217. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  218. package/dist/sensor/readout-timing.js +3 -57
  219. package/dist/sensor/readout-timing.js.map +1 -1
  220. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  221. package/dist/sensor/response-application-compatibility.js +3 -29
  222. package/dist/sensor/response-application-compatibility.js.map +1 -1
  223. package/dist/sensor/response-operating-range.d.ts +1 -0
  224. package/dist/sensor/response-operating-range.js +3 -79
  225. package/dist/sensor/response-operating-range.js.map +1 -1
  226. package/dist/sensor/responsivity-photocurrent.js +4 -108
  227. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  228. package/dist/sensor/sampling-aperture.js +1 -25
  229. package/dist/sensor/sampling-aperture.js.map +1 -1
  230. package/dist/sensor/spatial-sample-reduction.js +2 -19
  231. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  232. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  233. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  234. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  235. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  236. package/dist/sensor/spectral-coverage.d.ts +10 -1
  237. package/dist/sensor/spectral-coverage.js +1 -1
  238. package/dist/sensor/spectral-coverage.js.map +1 -1
  239. package/dist/sensor/spectral-quadrature.js +2 -35
  240. package/dist/sensor/spectral-quadrature.js.map +1 -1
  241. package/dist/sensor/spectral-response.js +1 -47
  242. package/dist/sensor/spectral-response.js.map +1 -1
  243. package/dist/stabilization/camera-shake.js +1 -6
  244. package/dist/stabilization/camera-shake.js.map +1 -1
  245. package/dist/stabilization/system.js +2 -80
  246. package/dist/stabilization/system.js.map +1 -1
  247. package/docs/AGENT_GUIDE.md +1 -0
  248. package/docs/API_REFERENCE.md +41 -2
  249. package/docs/QUICK_START.md +90 -8
  250. package/docs/USAGE.md +47 -2
  251. package/docs/api/core-version.md +1 -1
  252. package/docs/api/exports.json +53 -3
  253. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  254. package/docs/api/facade-develop-joined-parts.md +32 -0
  255. package/docs/api/facade-simulate-capture.md +36 -5
  256. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  257. package/docs/api/optics-lens-psf-profile.md +3 -1
  258. package/docs/api/output-developed-lens-corrections.md +12 -0
  259. package/docs/api/output-generic-lens-corrections.md +0 -9
  260. package/llms.txt +1 -0
  261. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,73 @@
1
1
  # Changelog
2
2
 
3
- ## 2.18.0 — unreleased (prepared November 9, 2026)
3
+ ## 2.19.0 — unreleased (prepared November 9, 2026)
4
+
5
+ A minor release of six issues: exposure time with an ND filter and generic ND presets (#578), the program line within the lens's f-number range (#583), very fast `lens.ideal` ranges (#585), developing a capture rendered in parts (#571), lens corrections in the development (#570) and the sampled-PSF orientation fix (#566). See the [2.19.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_19_0.md).
6
+
7
+ - **New:** `calculateFilteredExposureTime` and the generic ND presets `GENERIC_ND_FILTER_PROFILES` / `GENERIC_ND_FILTER_STOPS` (#578); `simulateCaptureFromParts` (#571); `output.lensCorrections` (#570); the progress stages `"diffraction-correction"` and `"sharpening"` (#571).
8
+ - **Changed:** program mode limits the generic program line to a lens range that does not hold f/4–f/16 instead of refusing it (#583); a `lens.ideal` range narrower than about f/1 now resolves in every exposure mode (#585).
9
+ - **Breaking fix (#566):** the rotationally symmetric `LensSampledPsfProfile` interpolation no longer mirrors asymmetric kernels in y (caller-supplied profiles in that mode only; no built-in lens uses it).
10
+ - **Root exports:** 10 new, 1,850 → 1,860 (runtime exports 626 → 631): 8 from #578 (4 runtime), 1 from #571 (runtime), 1 from #570 (type). `GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM` moves module (same value, still a root export).
11
+ - **Why minor:** new root exports and opt-in options; requests that 2.18.0 refused now resolve (#583, #585). #566 changes results only for caller-supplied profiles in the rotationally symmetric mode, a breaking fix the owner decided to ship in a minor release (2026-11-07, confirmed 2026-11-09). Nothing is removed, and no default changes.
12
+ - **Integration (this release):** `simulateCaptureFromParts` honors `output.lensCorrections` exactly as the whole-frame capture does (byte-identical; a part may keep or drop the option); the lens corrections run between the `diffraction-correction` and `sharpening` progress stages and report no stage of their own. 285 private copies of the same field checks and helpers in the domain parsers (for example `requireRecord` in 44 modules) now share one export each, and 35 identical private helpers found in two modules each are shared too; messages, error classes and results are unchanged.
13
+ - **Bundle:** the app's capture-worker shape (every chunk of `NATIVE_RAW_LIMITS`, `prepareDisplayView`, `serializeSimulatedCaptureDngFromParts`, `simulateCapture`, `simulateIdealReference`) is 351,775 B gzip (2.18.0: 351,859 B; limit 352,000 B, not raised). The six parts merged measured 353,811 B; sharing the duplicated helpers saved 2,036 B.
14
+
15
+ ### #566 LensSampledPsfProfile rotationally symmetric mode mirrors asymmetric PSFs in y
16
+
17
+ - **Breaking fix (owner decision 2026-11-07; confirmed 2026-11-09: a breaking fix, as this entry states):** the `rotationally-symmetric-radial-field-centroid-aligned` interpolation of `LensSampledPsfProfile` rotated each kernel as if its row index ran towards image −Y, while the environment PSF quadrature (`sensorPsfSourcePoint`) applies the row index as image +Y. At field azimuth θ the kernel was therefore turned to −θ: the y mirror of the intended kernel. It now rotates with the quadrature's convention, so an asymmetric kernel renders with the same orientation as a 2-D grid node declared at that field point.
18
+ - **Which results change:** only caller-supplied profiles that use this interpolation mode, at field points off the horizontal axis (`fieldYmm ≠ 0`), with a kernel that is not rotationally symmetric (a radial shift such as lateral color, radial elongation, coma, a decentered blur). Their `resolveLensSampledPsf` kernels (and the internal `resolveLensSampledPsfKernel`), and every capture, display view, RAW and convolution kernel built from them (`simulateCapture`, the native environment paths, `calculateSensorPsfIrradianceQuadrature`, `resolveLensPsfConvolutionKernels`), stop being mirrored in y. Kernels on the horizontal axis and rotationally symmetric kernels are unchanged (at θ = 0 and π the old and new rotations agree; a rotationally symmetric kernel is invariant). No built-in lens uses this mode (the generic lens catalogue uses the 2-D grid mode), so every hash lock holds. No API, schema or `src/gpu` change; the model id is unchanged.
19
+ - **Tests** (`test/lens-psf-interpolation.test.ts`): an asymmetric kernel (a radially shifted core plus a blob to the counter-clockwise side) resolved in the rotationally symmetric mode and as an exact 2-D grid node, at (±6, ±6) mm in all four quadrants; the light of a point source, placed through `sensorPsfSourcePoint`, has the declared radial (2.25 samples) and across-radius (0.6 samples) moments in both modes, the kernels differ by L1 < 0.25 (bilinear resampling) and the rotated kernel is far from the grid kernel's y mirror (L1 > 1). The test fails on 2.18.0. The test helpers now build kernels with the row index increasing with image +Y; the existing four-orientation rotation test, which used the old convention in its helpers, is unchanged otherwise.
20
+ - **Docs:** docs/PSF_FOUNDATION.md states the row convention of the rotation; docs/LENS_CATALOGUE.md drops the caveat; API reference regenerated (`LensSampledPsfInterpolation`).
21
+
22
+ ### #578 Exposure time with an ND filter
23
+
24
+ - **New: `calculateFilteredExposureTime({ shutterSeconds, filter, shutterDialSeconds?, wavelengthNanometers?, wavelengthBasis?, incidentPolarization? })`** (model `filtered-exposure-time` 1.0.0, for the app's Guide page on ND filters and the calculator's ND table): the shutter time that keeps the same exposure at the same f-number and ISO after adding front-of-lens filters, `shutterSeconds / T`, with `T` from the existing filter profiles through `composeFrontOfLensFilterTransmission` (one profile or a stack). Returns `filteredShutterSeconds`, `linearTransmissionFactor`, `attenuationStops`, `exposureFactor`, the full `filterTransmission` and, with a dial list, `dial` (nearest value in stops, shorter on a tie, its exposure error in stops, and `clamped` outside the dial). 1/125 s with a 10-stop filter gives 1024/125 ≈ 8.19 s, 8 s on `GENERIC_THIRD_STOP_SHUTTER_SECONDS`; an ND1000 (OD 3.0) gives 8 s. Exact under the linear sensor model (H = E·t, CIPA DC-004-2020 §5.5, EMVA 1288 §2); reciprocity failure is not modelled; nominal filters vary, and the envelope is an approximation whenever a profile is. Filters are resolved at 550 nm by default (neutral filters do not depend on it).
25
+ - **New: generic ND presets `GENERIC_ND_FILTER_PROFILES[3 | 6 | 10]` and `GENERIC_ND_FILTER_STOPS`** (owner decision 2026-11-09): frozen, unbranded front-of-lens filter profiles (`generic-nd-3-stop`, `generic-nd-6-stop`, `generic-nd-10-stop`) that transmit exactly 2^-N (1/8, 1/64, 1/1024; "N-stop" means exactly 2^-N), spectrally flat (`neutral-linear-transmission`) over 380–780 nm in air, and marked idealized (`scientificStatus: "approximation"`, uncertainty not quantified, evidence note "Idealized"). 1/125 s with the 10-stop preset gives exactly 8.192 s, 8 s on the third-stop dial.
26
+ - **Root exports:** 8 new (4 runtime): `calculateFilteredExposureTime`, `FILTERED_EXPOSURE_DEFAULT_WAVELENGTH_NANOMETERS`, `GENERIC_ND_FILTER_PROFILES`, `GENERIC_ND_FILTER_STOPS` and the types `CalculateFilteredExposureTimeInput`, `FilteredExposureTime`, `FilteredExposureDialSetting`, `GenericNdFilterStops`. Pure function and constant data; no capture, RAW or bundle change.
27
+ - **Docs and tests:** docs/PHYSICS_FOUNDATION.md ("Exposure time with an ND filter"), docs/USAGE.md (with a runnable example; later USAGE.md fragment indices in docs/validation/documentation-fragments.json shift by one), docs/AGENT_GUIDE.md, llms.txt, API reference task index; `test/filtered-exposure-time.test.ts` (including exact preset values, flatness, freezing and parser round trip).
28
+
29
+ ### #583 Program mode: the program line follows the lens's f-number range
30
+
31
+ - **Changed (requests that 2.18.0 refused now resolve):** in `exposure.mode: "program"`, `simulateCapture`, `simulateDisplayView` and `prepareDisplayView` limit the engine's generic program line (f/16 1/2000 s … f/8 1/125 s … f/4 1.024 s) to the lens's f-number range when the range does not hold f/4–f/16, where 2.18.0 refused it ("Program-line aperture lies outside the resolved equipment capability range."). This lifts the 2.18.0 limitation for `lens.ideal` `widestFNumber`/`narrowestFNumber` (#576; deferred to 2.19.0 by the owner on 2026-11-09), and applies equally to the `lens.prime` and `lens.catalogue` ranges that do not hold the line: the 800 and 1200 mm-equivalent primes, *PV 800 5.6 Pro*, *PV 1200 8 Pro*, *PV 200 5.6*, *PV 300 5.6*, *PV 400 5.6*, *PV 500 5.6*, *PV 600 6.3* and *PV 800 9*.
32
+ - **Model (an approximation; a generic line, not a manufacturer's program):** the line's f/4 and f/16 are clamped to the range, giving the same shape: the aperture held at the range's end and the shutter keeping each point's exposure (t/N², camera equation H = 0.65·L·t/N²), still from 1/2000 s to 1.024 s. Where both lines run, the aperture is the old line's clamped in log2 to the range; beyond the line's ends the resolver holds them as before (Auto ISO raises the ISO; a fixed ISO under- or overexposes, reported by `standardExposureDeviationStops`). Example: f/8–f/32 gives f/16 1/2000 s, f/16 1/125 s, f/8 1/125 s, f/8 1.024 s. The `exposure.programLine` assumption records a limited line's profile (id `"facade-generic-program-line-lens-range"`, with its nodes) (`defaultProgramLine` in `src/facade/simulate-capture.ts`).
33
+ - **Unchanged:** requests with no range, and every lens whose range holds f/4–f/16, keep the line, its assumption and their bytes (pinned SHA-256 digests of samples, RAW codes, exposure and assumptions from 3f9d65b for the tier lens, Auto ISO, `lens.ideal` without and with a holding range, `lens.prime: 50` and `pv-50-1.8`). No root export added; no `src/gpu` change. The app's capture-worker shape measured 351,989 B gzip with this part alone (2.18.0: 351,859 B; limit 352,000 B, not raised).
34
+ - **Owner decision (2026-11-09):** keep the line's 1/2000–1.024 s span; the limited line applies to `lens.prime` and `lens.catalogue` ranges as well as `lens.ideal`.
35
+ - **Tests:** `test/program-line-lens-range.test.ts` (byte pins; the limited line equals the old line clamped to the range at the same exposure at 201 points for ten ranges, from 1/2000 s to 1.024 s; program resolves within the range for seven ideal ranges at 5, 2000 and 200,000 lx; the 800 mm prime and *PV 800 9*); `test/ideal-lens.test.ts` updated (the refusal is now an exposure).
36
+ - **Docs:** docs/QUICK_START.md ("Program with a range"), docs/USAGE.md ("The capture facade's program line and the lens's f-number range").
37
+
38
+ ### #585 lens.ideal: a range narrower than about f/1 is refused in every exposure mode
39
+
40
+ - **Fix (requests that 2.18.0 refused now resolve):** a `lens.ideal` range whose narrowest f-number N0 is below about f/1 (for example f/0.5–f/0.9) was refused in every exposure mode ("referenceExposure.shutterSeconds lies outside the resolved equipment capability range."): #569's exposure anchor at the range's aperture nearest f/8 with the same N²/t needs t = 1/125 s × (N0/8)², shorter than the body's shortest shutter (1/9877 s at f/0.9 against 1/8000 s). The anchor now takes the body's shortest shutter when that t is shorter, a lower N²/t. The anchor is only where the stops are measured from (CIPA DC-004 reference luminance from the camera equation at the anchor, the meter's relative signals and the resolvers all use it), so exposures are the same as with an f/8 anchor: aperture and shutter priority at f/0.7 and 1/4000 s resolve the open ideal lens's settings to 1e-12. The #583 program line's node stops are measured from the anchor. The `exposure.referenceExposure` explanation says "the shortest shutter at f/N0" in that case. Owner decision 2026-11-09: fix in 2.19.0.
41
+ - **Unchanged:** every request that resolved before gives the same bytes (SHA-256 pins of samples, RAW codes, exposure and assumptions for ten requests, including anchors off f/8 at f/9 and f/4 and a fixed f/1 lens). The app's capture-worker shape measures 351,990 B gzip (limit 352,000 B, not raised); the fix's code was paid for by sharing the range clamp between the anchor, the program line and the f-number check, and by a shorter program-line node table.
42
+ - **Tests:** `test/program-line-lens-range.test.ts` (f/0.5–f/0.9 in program with fixed and Auto ISO, aperture priority, shutter priority and manual; the pins).
43
+ - **Docs:** docs/QUICK_START.md ("An optional f-number range"), docs/USAGE.md.
44
+
45
+ ### #571 Develop joined parts: whole-image sharpening and diffraction correction
46
+
47
+ - **New: `simulateCaptureFromParts(options, parts)`** (#571, for Photivra/apps#253's "Applying … to the whole image" step): develops the whole frame of `options` from region renders of the same request (the `SimulateCaptureDngPart` records `serializeSimulatedCaptureDngFromParts` takes, joined by the same code with the same checks), including `output.diffractionCorrection` and `output.sharpening`, which a region refuses. The joined RAW is the whole render's RAW, so `image.samples`, the JPEG or PNG bytes, `raw` and `plan` equal `simulateCapture(options)` bit for bit. Differences, documented: `diagnostics.providerEvaluationCount` is 0, `timingsMs` are the call's own (`raw` is the join), `diagnostics.fast` and the `raw.*` assumptions stay in the parts' results, and a new `parts` assumption (`{ partCount }`) records the join.
48
+ - **Progress:** `SimulateCaptureProgress.stage` gains `"diffraction-correction"` and `"sharpening"`, one event per output row of the whole image (last event `completedTileCount === totalTileCount`), reported by `simulateCapture` too when those options are on; the stages yield to the host about every 8 ms, so `signal` now cancels them. `simulateCaptureFromParts` reports no `raw` stage. Code with an exhaustive `switch` over `stage` sees two new values.
49
+ - **Memory:** the detail stages run row by row in place (`correctDiffractionInPlace`: an (R + 1)-row ring; `renderSdrWithUnsharpMaskInPlace`: each row encoded in place by the native SDR kernel, which takes `maximum` 0 for unquantized output, and a (2r + 1)-row ring of the horizontal pass), with the same sums in the same order: one developed image in memory instead of up to six. At 24 MP the whole `simulateCapture` with both stages peaked at 924 MiB (2.18.0: 3,670 MB, resident set sampled every 50 ms). Captures, files, diagnostics and assumptions of every request without `simulateCaptureFromParts` are unchanged (byte comparison of six detail requests against 2.18.0's build: JPEG, 16-bit, thresholded mask, Display P3 PNG, AHD with auto white balance and a push, a 3 × 2 image with σ = 8).
50
+ - **Measured at 24 MP** (6000 × 4000 at 6 µm, slanted edge at f/8 with `lens.diffraction`, 8 parts; Apple M5 Pro, Node.js 24.19.0, one thread, shared machine, two runs; `node scripts/benchmark-joined-development.mjs`): join and develop 1.9–2.2 s and 532–551 MiB peak without a detail stage; with sharpening 3.3–3.7 s, with diffraction correction (R = 3) 3.7–3.9 s, with both 5.1–5.4 s (development 1.2 s, correction 2.0–2.1 s, sharpening 1.7–1.8 s), 1,079–1,080 MiB peak each; the whole-frame `simulateCapture` with both took 10.0–10.4 s. The peak includes the parts' RAW and the joined RAW (7 bytes per native site each) and one 64-bit developed image (549 MiB).
51
+ - **Root exports:** 1 new (runtime), 1,850 → 1,851: `simulateCaptureFromParts` (new module `src/facade/develop-joined-parts.ts`), named in the `simulate*` family of docs/API_STYLE.md (orchestration returning a `SimulateCaptureResult`), since "develop" is not one of the documented verb families. Messages of the parts join now read "Part k: …" and "The parts …" (were "DNG part k: …" and "The DNG parts …").
52
+ - **Decisions (coordinator, 2026-11-09):** the name `simulateCaptureFromParts`; the extra Native performance workflow run that the change to `src/output/native-capture-sdr.ts` triggers is accepted; the two new progress stages count as additive (code with an exhaustive `switch` over `stage` must add them).
53
+ - **Bundle (part branch):** the app's capture-worker shape was 351,929 B gzip (2.18.0: 351,852 B; limit 352,000 B, unchanged); with `simulateCaptureFromParts` added to the worker's imports, 351,937 B. The row-by-row stages add about 250 B; reusing the native SDR kernel for the unsharp mask's encoding and sharing the facade's row-progress and wide-gamut encoding code save about 170 B.
54
+ - **Tests:** `test/simulate-capture-develop-parts.test.ts` (whole frame against joined parts byte for byte on six requests: JPEG with both stages, 16-bit with a thresholded mask, AHD with auto white balance, a push and the four-spot filter, Display P3 PNG with correction, DNG with both stages, no stage; progress order and rows; refusals and abort; the in-place stages against whole-image passes on odd sizes, R up to 16 on a 12 × 20 image).
55
+ - **Docs:** docs/QUICK_START.md ("Developing a capture rendered in parts (#571)", the `onProgress` row, the sharpening section), docs/USAGE.md; API reference regenerated.
56
+
57
+ ### #570 Development option: apply lens corrections to the JPEG
58
+
59
+ - **New: `output.lensCorrections: { distortion?, lateralColor?, vignetting? }`** (#570, for Photivra/apps#253 story 3.1), with `lens.catalogue`: the lens's generic correction profile (`createGenericLensCorrectionProfile`, #549) applied to the developed linear colour (after demosaic, colour, white balance and `diffractionCorrection`; before the tone curve and `sharpening`), for the JPEG, PNG and `image.samples`. Each correction is off unless `true`; all off (or omitted) develops byte for byte as before. **The RAW and the DNG are never corrected** (tested: identical DNG bytes).
60
+ - **Model** (labelled approximation, the new `output.lensCorrections` assumption): inverse mapping s = q(1 + k1|q|²/R²), q = m·a_c·p, bilinear, one resampling per channel; the image keeps its size and is magnified by 1/m, the smallest that leaves no missing pixel (1 for the barrel wide angles, 1.017 for *PV 300 5.6*). Vignetting is corrected to a flat field with det J/E at the source point, E the lens's relative illumination and 1/det J its distortion's area factor, which the capture applies (flat to 0.03 %; E alone would leave a *PV 16 2.8* corner about 18 % bright). The kernel matches `calculateLensCorrectedCapture` to 1e-12 for distortion and lateral colour.
61
+ - **Regions and parts:** byte-identical to the whole frame (barrel, pincushion with fill, AHD 16-bit PNG). A region develops the frame pixels its corrected pixels read (`developedRect`; `region.nativeRect`, `raw` and `plan` describe that rectangle).
62
+ - **Trade-offs measured through the option** (#552's): distortion correction × 0.706 corner detail (centre × 0.998); vignetting correction raises corner signal × 2.44 and noise × 2.50 (SNR unchanged).
63
+ - **Not offered** by `simulateDisplayView` or `simulateIdealReference` (refused by name); refused without `lens.catalogue`.
64
+ - **Cost:** about 1.3 s per 24 MP frame on one thread (Apple M5 Pro), loaded with the catalogue chunk on demand.
65
+ - **API:** one new root type, `SimulateCaptureLensCorrections`; `SIMULATE_CAPTURE_COST_OPTIONS` gains `"output.lensCorrections"`. `GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM` now lives in `src/output/developed-lens-corrections.ts` (same value, still a root export).
66
+ - **Owner decisions (2026-11-09):** zoom to fill (no missing pixels, no change of output size); the vignetting correction includes the distortion's area factor (the correction profile's own vignetting component, which holds E only, is follow-up #584); the display view refuses `output.lensCorrections` for now; the region rectangles (`developedRect` in the assumption; `region.nativeRect`, `raw` and `plan` for the developed rectangle) are accepted as they are.
67
+ - **Bundle:** on its own the part grew the app's capture-worker shape by 1,735 B gzip (entry +756 B, catalogue chunk +979 B), over the 352,000 B limit; the integration's shared helpers bring the release under it (see the 2.19.0 summary). The limit is not raised.
68
+ - **Docs and tests:** docs/DIGITAL_OPTICS_FOUNDATION.md ("Lens corrections in the development"), docs/QUICK_START.md, docs/USAGE.md; `test/simulate-capture-lens-corrections.test.ts`.
69
+
70
+ ## 2.18.0 — published November 9, 2026
4
71
 
5
72
  A minor release of four issues: equivalence numbers for teaching (#573), an ideal lens at any physical focal length and f-number (#576), catalogue lenses in the display view (#569) and fast catalogue lens captures (#568). Captures, RAW codes, files and assumptions are the same as 2.17.0 for every request that 2.17.0 renders and that uses none of the new options (every hash lock holds).
6
73
  - **New:** `calculateSensorEquivalence` and `calculateBlurShareOfFrame` (#573); `lens.ideal: { focalLengthMm, widestFNumber?, narrowestFNumber? }` and `IDEAL_LENS_LIMITS` (#576; the optional f-number range added at the owner's request, 2026-11-09); `simulateDisplayView` and `prepareDisplayView` render `lens.catalogue` (#569); a fast method for `lens.catalogue` behind `fast` or `lens.catalogue: { kind, fast }` (#568).
package/CITATION.cff CHANGED
@@ -9,7 +9,7 @@ abstract: "An open TypeScript engine for the science and mathematics of photogra
9
9
  repository-code: "https://github.com/photivra/photivra"
10
10
  url: "https://photivra.com"
11
11
  license: "Apache-2.0"
12
- version: "2.18.0"
12
+ version: "2.19.0"
13
13
  keywords:
14
14
  - photography
15
15
  - camera optics
package/README.md CHANGED
@@ -32,10 +32,10 @@ The source is licensed under Apache-2.0.
32
32
 
33
33
  ## Quick start
34
34
 
35
- Install the published ESM package below. The [2.18.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_18_0.md) and [changelog](https://github.com/Photivra/photivra/blob/main/CHANGELOG.md) describe the current release:
35
+ Install the published ESM package below. The [2.19.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_19_0.md) and [changelog](https://github.com/Photivra/photivra/blob/main/CHANGELOG.md) describe the current release:
36
36
 
37
37
  ```sh
38
- npm install @photivra/engine@2.18.0
38
+ npm install @photivra/engine@2.19.0
39
39
  ```
40
40
 
41
41
  Simulate a photograph of a test chart with a generic camera, then read the developed sRGB pixels:
@@ -75,7 +75,7 @@ console.log(horizontal.provenance);
75
75
 
76
76
  Start with the [Developer Guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Then use the [Usage Guide](https://github.com/Photivra/photivra/blob/main/docs/USAGE.md) and the [API reference](https://github.com/Photivra/photivra/blob/main/docs/API_REFERENCE.md), which is generated from the package exports.
77
77
 
78
- Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.18.0` tag.
78
+ Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.19.0` tag.
79
79
 
80
80
  ## Documentation
81
81
 
@@ -363,13 +363,13 @@ The application can then use those results, while the photography calculations t
363
363
 
364
364
  ## Status
365
365
 
366
- - Repository package version: `2.18.0` (release candidate prepared November 9, 2026; publication is separate)
367
- - Engine API contract: `2.18.0`
366
+ - Repository package version: `2.19.0` (release candidate prepared November 9, 2026; publication is separate)
367
+ - Engine API contract: `2.19.0`
368
368
  - Versioned generic tier reference assets: [GENERIC_TIER_PRESETS.md](https://github.com/Photivra/photivra/blob/main/docs/GENERIC_TIER_PRESETS.md).
369
369
  - Composed POC simulation API contract: `1.0.0`
370
370
  - Stability: 1.0 root-package compatibility policy; bounded scientific models and independent POC contract
371
371
 
372
- From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.18.0; existing archives keep their original creator identities. Schema, model and POC contract versions stay independent.
372
+ From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.19.0; existing archives keep their original creator identities. Schema, model and POC contract versions stay independent.
373
373
 
374
374
  A breaking change to the root contracts now needs a new package major version. Additive and corrective changes follow [compatibility and migration](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_1_3_0.md). A 1.0 package is not a claim of calibrated physical accuracy.
375
375
 
package/SECURITY.md CHANGED
@@ -4,7 +4,7 @@ Please do not open a public issue that contains vulnerability details.
4
4
 
5
5
  ## Supported versions
6
6
 
7
- The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.17.0**, published on November 8, 2026 (see [CHANGELOG.md](CHANGELOG.md)).
7
+ The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.18.0**, published on November 9, 2026 (see [CHANGELOG.md](CHANGELOG.md)).
8
8
 
9
9
  Security fixes target the current maintained development and release line. Do not assume that older 1.x releases, snapshots or pre-1.0 releases will receive fixes.
10
10
 
@@ -14,7 +14,8 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
14
14
  import { parseEvidenceList } from "../core/evidence-provenance.js";
15
15
  import { InvalidScientificInputError } from "../core/validation.js";
16
16
  import { parseFocusPlane } from "../optics/focus-state.js";
17
- import { requireInputObject, requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
17
+ import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireTrimmedConfigString as requireScientificNonEmptyString, requireInputNonNegativeFinite as requireScientificNonNegativeFinite, requireInputPositiveFinite as requireScientificPositiveFinite, parseConfigLimitations as parseLimitations } from "../core/record-validation.js";
18
+ import { deepFreeze, cloneJson } from "../core/shared-helpers.js";
18
19
  export const FOCUS_CONTROL_PROFILE_SCHEMA_VERSION = "0.1.0";
19
20
  export const FOCUS_CONTROL_STATE_VERSION = "0.1.0";
20
21
  const FOCUS_MODES = new Set([
@@ -33,65 +34,6 @@ const TARGET_LOSS_REASONS = new Set([
33
34
  "identity-lost",
34
35
  "outside-focus-area"
35
36
  ]);
36
- function requireRecord(value, path, allowedKeys) {
37
- if (typeof value !== "object" ||
38
- value === null ||
39
- Array.isArray(value)) {
40
- throw new InvalidConfigurationError(path + " must be an object.");
41
- }
42
- if (allowedKeys !== null)
43
- rejectUnknownFields(value, allowedKeys, path);
44
- return value;
45
- }
46
- function requireNonEmptyString(value, path) {
47
- if (typeof value !== "string" ||
48
- value.trim().length === 0) {
49
- throw new InvalidConfigurationError(path +
50
- " must be a non-empty string.");
51
- }
52
- return value.trim();
53
- }
54
- function requireScientificNonEmptyString(value, path) {
55
- if (typeof value !== "string" ||
56
- value.trim().length === 0) {
57
- throw new InvalidConfigurationError(path +
58
- " must be a non-empty string.");
59
- }
60
- return value.trim();
61
- }
62
- function requireScientificNonNegativeFinite(value, path) {
63
- if (typeof value !== "number" ||
64
- !Number.isFinite(value) ||
65
- value < 0) {
66
- throw new InvalidScientificInputError(path +
67
- " must be finite and greater than or equal to zero.");
68
- }
69
- return value;
70
- }
71
- function requireScientificPositiveFinite(value, path) {
72
- if (typeof value !== "number" ||
73
- !Number.isFinite(value) ||
74
- value <= 0) {
75
- throw new InvalidScientificInputError(path +
76
- " must be finite and greater than zero.");
77
- }
78
- return value;
79
- }
80
- function parseLimitations(value, path) {
81
- if (!Array.isArray(value)) {
82
- throw new InvalidConfigurationError(path + " must be an array.");
83
- }
84
- const parsed = value.map((entry, index) => requireNonEmptyString(entry, path +
85
- "[" +
86
- index +
87
- "]"));
88
- if (new Set(parsed).size !==
89
- parsed.length) {
90
- throw new InvalidConfigurationError(path +
91
- " must not contain duplicates.");
92
- }
93
- return parsed;
94
- }
95
37
  function parseUniqueEnumList(value, path, allowed) {
96
38
  if (!Array.isArray(value) ||
97
39
  value.length === 0) {
@@ -167,21 +109,6 @@ export function parseFocusControlProfile(value) {
167
109
  limitations: parseLimitations(record.limitations, "focusControlProfile.limitations")
168
110
  };
169
111
  }
170
- function cloneJson(value) {
171
- return JSON.parse(JSON.stringify(value));
172
- }
173
- function deepFreeze(value) {
174
- if (typeof value !== "object" ||
175
- value === null ||
176
- Object.isFrozen(value)) {
177
- return value;
178
- }
179
- Object.freeze(value);
180
- for (const child of Object.values(value)) {
181
- deepFreeze(child);
182
- }
183
- return value;
184
- }
185
112
  function immutableState(state) {
186
113
  return deepFreeze(cloneJson(state));
187
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"focus-control.js","sourceRoot":"","sources":["../../src/capture/focus-control.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Focus control: manual focus (MF), single autofocus (AF-S) and continuous autofocus (AF-C), focus\n * lock, and the focus-priority or release-priority check before the shutter fires.\n *\n * Focus is always a distance along the optical axis (a focus plane) or infinity, never a distance\n * on screen. The focus \"actuator\" is ideal: it focuses on the target observations it is given. It\n * does not model a real AF sensor, a focus-ring scale or how long focusing takes. Losing a target,\n * reacquiring it, locking focus and the release-priority decision are separate state changes.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseFocusPlane,\n type FocusPlane\n} from \"../optics/focus-state.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const FOCUS_CONTROL_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\nexport const FOCUS_CONTROL_STATE_VERSION =\n \"0.1.0\" as const;\n\nexport type FocusControlMode =\n | \"manual\"\n | \"single-af\"\n | \"continuous-af\";\n\nexport type FocusAcquisitionState =\n | \"idle\"\n | \"acquiring\"\n | \"acquired\"\n | \"target-lost\"\n | \"locked\";\n\nexport type FocusReleasePriority =\n | \"focus-priority\"\n | \"release-priority\"\n | \"balanced\";\n\nexport type FocusTargetLossReason =\n | \"occluded\"\n | \"out-of-frame\"\n | \"identity-lost\"\n | \"outside-focus-area\";\n\nexport interface FocusControlProfile {\n schemaVersion:\n typeof FOCUS_CONTROL_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n scientificStatus: \"approximation\";\n supportedModes:\n readonly FocusControlMode[];\n actuator: {\n kind: \"ideal-instantaneous\";\n evidence:\n readonly EvidenceProvenance[];\n };\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\";\n supportedReleasePriorities:\n readonly FocusReleasePriority[];\n evidence:\n readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\ninterface FocusTargetObservationBase {\n targetId: string;\n focusAreaId?: string;\n observedAtSeconds: number;\n}\n\nexport type FocusTargetObservation =\n | (FocusTargetObservationBase & {\n kind: \"finite-surface\";\n /**\n * Distance along the camera's optical axis (the same convention as\n * FocusPlane), not the renderer's ray length to the point.\n */\n longitudinalDistanceM: number;\n })\n | (FocusTargetObservationBase & {\n kind:\n \"infinity-environment\";\n })\n | (FocusTargetObservationBase & {\n kind: \"unavailable\";\n reason:\n FocusTargetLossReason;\n });\n\n/**\n * Immutable focus-controller state for MF, AF-S or AF-C.\n *\n * The resolved focus is a finite distance along the optical axis in metres, or infinity.\n * Acquisition, loss, reacquisition and lock records explain why a target can be used. The\n * focus-release check is separate from shutter sequencing. The ideal actuator does not model\n * real AF accuracy or the physical time focusing takes.\n */\nexport interface FocusControlState {\n version:\n typeof FOCUS_CONTROL_STATE_VERSION;\n stateId: string;\n sourceProfile: {\n profileId: string;\n profileVersion: string;\n };\n profileLimitations:\n readonly string[];\n mode:\n FocusControlMode;\n acquisitionState:\n FocusAcquisitionState;\n focus:\n FocusPlane;\n activeTargetId?: string;\n activeFocusAreaId?: string;\n lastEventTimeSeconds: number;\n focusResolvedAtSeconds:\n number;\n locked: boolean;\n lock?: {\n lockedAtSeconds: number;\n resumeAcquisitionState:\n Exclude<\n FocusAcquisitionState,\n \"locked\"\n >;\n };\n actuatorModel:\n \"ideal-instantaneous\";\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\";\n exposureModified: false;\n meteringModified: false;\n whiteBalanceModified: false;\n subjectTrackingRecognitionModeled:\n false;\n rendererCoordinatesConsumed:\n false;\n limitations:\n readonly string[];\n}\n\nexport interface CreateFocusControlStateInput {\n stateId: string;\n profile:\n FocusControlProfile;\n mode:\n FocusControlMode;\n initialFocus:\n FocusPlane;\n initialTimeSeconds?: number;\n}\n\nexport interface SetManualFocusStateInput {\n state:\n FocusControlState;\n stateId: string;\n focus:\n FocusPlane;\n eventTimeSeconds: number;\n commandId?: string;\n}\n\nexport type FocusTargetControlEvent =\n | \"acquire\"\n | \"update\";\n\nexport interface ResolveFocusTargetObservationInput {\n state:\n FocusControlState;\n stateId: string;\n event:\n FocusTargetControlEvent;\n observation:\n FocusTargetObservation;\n}\n\nexport interface FocusTargetResolution {\n state:\n FocusControlState;\n targetObservationApplied:\n boolean;\n focusChanged: boolean;\n acquisitionEvent:\n FocusTargetControlEvent;\n actuatorModel:\n \"ideal-instantaneous\";\n silentRetargetingPerformed:\n false;\n}\n\nexport interface SetFocusLockInput {\n state:\n FocusControlState;\n stateId: string;\n locked: boolean;\n eventTimeSeconds: number;\n}\n\nexport interface AssessFocusReleaseGateInput {\n state:\n FocusControlState;\n profile:\n FocusControlProfile;\n priority:\n FocusReleasePriority;\n}\n\nexport interface FocusReleaseGateAssessment {\n priority:\n FocusReleasePriority;\n releaseAuthorized: boolean;\n reason:\n | \"manual-focus-state\"\n | \"focus-acquired\"\n | \"focus-locked\"\n | \"release-priority-bypasses-focus-gate\"\n | \"focus-not-acquired\"\n | \"balanced-policy-not-modeled\";\n requiresProfileSpecificBalancedPolicy:\n boolean;\n opticalFocusModified: false;\n exposureModified: false;\n}\n\nconst FOCUS_MODES =\n new Set<FocusControlMode>([\n \"manual\",\n \"single-af\",\n \"continuous-af\"\n ]);\n\nconst RELEASE_PRIORITIES =\n new Set<FocusReleasePriority>([\n \"focus-priority\",\n \"release-priority\",\n \"balanced\"\n ]);\n\nconst TARGET_LOSS_REASONS =\n new Set<FocusTargetLossReason>([\n \"occluded\",\n \"out-of-frame\",\n \"identity-lost\",\n \"outside-focus-area\"\n ]);\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireScientificNonEmptyString(\n value: string,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireScientificNonNegativeFinite(\n value: number,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than or equal to zero.\"\n );\n }\n return value;\n}\n\nfunction requireScientificPositiveFinite(\n value: number,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than zero.\"\n );\n }\n return value;\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed =\n value.map((entry, index) =>\n requireNonEmptyString(\n entry,\n path +\n \"[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseUniqueEnumList<T extends string>(\n value: unknown,\n path: string,\n allowed: ReadonlySet<T>\n): readonly T[] {\n if (\n !Array.isArray(value) ||\n value.length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-empty array.\"\n );\n }\n const parsed =\n value.map((entry, index) => {\n if (\n typeof entry !==\n \"string\" ||\n !allowed.has(entry as T)\n ) {\n throw new InvalidConfigurationError(\n path +\n \"[\" +\n index +\n \"] is invalid.\"\n );\n }\n return entry as T;\n });\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\n/**\n * Validates an untrusted focus-control profile and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Focus is a distance along the optical axis or infinity, never a distance on screen. The ideal\n * actuator focuses on the target observations it is given. It does not model a real AF sensor, a\n * focus-ring scale or focusing time.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The validated profile. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseFocusControlProfile(\n value: unknown\n): FocusControlProfile {\n const record =\n requireRecord(\n value,\n \"focusControlProfile\", [\"schemaVersion\", \"scientificStatus\", \"actuator\", \"continuousTargetLossPolicy\", \"profileId\", \"profileVersion\", \"supportedModes\", \"supportedReleasePriorities\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.schemaVersion must be \"' +\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.scientificStatus must be \"approximation\".'\n );\n }\n\n const actuator =\n requireRecord(\n record.actuator,\n \"focusControlProfile.actuator\", [\"kind\", \"evidence\"]\n );\n if (\n actuator.kind !==\n \"ideal-instantaneous\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.actuator.kind must be \"ideal-instantaneous\" in schema 0.1.0.'\n );\n }\n if (\n record\n .continuousTargetLossPolicy !==\n \"hold-last-focus-require-explicit-reacquisition\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.continuousTargetLossPolicy must be \"hold-last-focus-require-explicit-reacquisition\".'\n );\n }\n\n return {\n schemaVersion:\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"focusControlProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"focusControlProfile.profileVersion\"\n ),\n scientificStatus:\n \"approximation\",\n supportedModes:\n parseUniqueEnumList(\n record.supportedModes,\n \"focusControlProfile.supportedModes\",\n FOCUS_MODES\n ),\n actuator: {\n kind:\n \"ideal-instantaneous\",\n evidence:\n parseEvidenceList(\n actuator.evidence,\n \"focusControlProfile.actuator.evidence\"\n )\n },\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\",\n supportedReleasePriorities:\n parseUniqueEnumList(\n record\n .supportedReleasePriorities,\n \"focusControlProfile.supportedReleasePriorities\",\n RELEASE_PRIORITIES\n ),\n evidence:\n parseEvidenceList(\n record.evidence,\n \"focusControlProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"focusControlProfile.limitations\"\n )\n };\n}\n\nfunction cloneJson<T>(\n value: T\n): T {\n return JSON.parse(\n JSON.stringify(value)\n ) as T;\n}\n\nfunction deepFreeze<T>(\n value: T\n): T {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Object.isFrozen(value)\n ) {\n return value;\n }\n\n Object.freeze(value);\n for (\n const child of\n Object.values(\n value as UnknownRecord\n )\n ) {\n deepFreeze(child);\n }\n return value;\n}\n\nfunction immutableState(\n state:\n FocusControlState\n): FocusControlState {\n return deepFreeze(\n cloneJson(state)\n );\n}\n\nfunction stateBase(\n stateId: string,\n profile:\n FocusControlProfile,\n mode:\n FocusControlMode,\n focus:\n FocusPlane,\n acquisitionState:\n FocusAcquisitionState,\n eventTimeSeconds: number,\n focusResolvedAtSeconds:\n number,\n activeTargetId?:\n string,\n activeFocusAreaId?:\n string,\n lock?:\n FocusControlState[\"lock\"]\n): FocusControlState {\n return immutableState({\n version:\n FOCUS_CONTROL_STATE_VERSION,\n stateId:\n requireScientificNonEmptyString(\n stateId,\n \"stateId\"\n ),\n sourceProfile: {\n profileId:\n profile.profileId,\n profileVersion:\n profile.profileVersion\n },\n profileLimitations: [\n ...profile.limitations\n ],\n mode,\n acquisitionState,\n focus:\n parseFocusPlane(focus),\n ...(activeTargetId ===\n undefined\n ? {}\n : {\n activeTargetId\n }),\n ...(activeFocusAreaId ===\n undefined\n ? {}\n : {\n activeFocusAreaId\n }),\n lastEventTimeSeconds:\n eventTimeSeconds,\n focusResolvedAtSeconds,\n locked:\n acquisitionState ===\n \"locked\",\n ...(lock === undefined\n ? {}\n : {\n lock\n }),\n actuatorModel:\n \"ideal-instantaneous\",\n continuousTargetLossPolicy:\n profile\n .continuousTargetLossPolicy,\n exposureModified: false,\n meteringModified: false,\n whiteBalanceModified:\n false,\n subjectTrackingRecognitionModeled:\n false,\n rendererCoordinatesConsumed:\n false,\n limitations: [\n ...profile.limitations,\n \"Focus target observations use longitudinal camera-space/conjugate distance, not renderer ray length.\",\n \"Ideal instantaneous focus actuation is an approximation and does not represent commercial AF speed, error, hunting, recognition, or low-light behavior.\",\n \"Focus control does not modify exposure, metering, white balance, or scene illumination.\"\n ]\n });\n}\n\nfunction requireSupportedMode(\n profile:\n FocusControlProfile,\n mode:\n FocusControlMode\n): void {\n if (\n !profile.supportedModes\n .includes(mode)\n ) {\n throw new InvalidScientificInputError(\n \"Selected focus-control mode is not supported by the profile.\"\n );\n }\n}\n\nfunction requireMonotonicEventTime(\n current:\n FocusControlState,\n eventTimeSeconds: number\n): number {\n const parsed =\n requireScientificNonNegativeFinite(\n eventTimeSeconds,\n \"eventTimeSeconds\"\n );\n if (\n parsed <\n current.lastEventTimeSeconds\n ) {\n throw new InvalidScientificInputError(\n \"Focus-control event time must not move backward.\"\n );\n }\n return parsed;\n}\n\nfunction verifyStateProfile(\n state:\n FocusControlState,\n profile:\n FocusControlProfile\n): void {\n if (\n state.sourceProfile\n .profileId !==\n profile.profileId ||\n state.sourceProfile\n .profileVersion !==\n profile.profileVersion\n ) {\n throw new InvalidScientificInputError(\n \"Focus-control state/profile identity does not match.\"\n );\n }\n}\n\n/**\n * Creates the initial ideal focus-controller state from a profile, a focus plane and a public\n * state ID.\n * @param input - New state ID, profile, focus mode, initial focus plane and optional start time.\n * @returns The initial state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function createFocusControlState(\n input:\n CreateFocusControlStateInput\n): FocusControlState {\n requireInputKeys(input, \"createFocusControlState\", [\"stateId\", \"profile\", \"mode\", \"initialFocus\", \"initialTimeSeconds\"]);\n const profile =\n parseFocusControlProfile(\n input.profile\n );\n requireSupportedMode(\n profile,\n input.mode\n );\n const time =\n input.initialTimeSeconds ===\n undefined\n ? 0\n : requireScientificNonNegativeFinite(\n input.initialTimeSeconds,\n \"initialTimeSeconds\"\n );\n\n return stateBase(\n input.stateId,\n profile,\n input.mode,\n input.initialFocus,\n \"idle\",\n time,\n time\n );\n}\n\nfunction stateProfileView(\n state:\n FocusControlState\n): FocusControlProfile {\n return {\n schemaVersion:\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION,\n profileId:\n state.sourceProfile\n .profileId,\n profileVersion:\n state.sourceProfile\n .profileVersion,\n scientificStatus:\n \"approximation\",\n supportedModes: [\n state.mode\n ],\n actuator: {\n kind:\n \"ideal-instantaneous\",\n evidence: []\n },\n continuousTargetLossPolicy:\n state\n .continuousTargetLossPolicy,\n supportedReleasePriorities: [\n \"focus-priority\",\n \"release-priority\",\n \"balanced\"\n ],\n evidence: [],\n limitations:\n state.profileLimitations\n };\n}\n\n/**\n * Returns the state after manually focusing on the given finite or infinity plane. No lens\n * actuator or focus-ring conversion is modelled.\n * @param input - Current state, new state ID, new focus plane and event time.\n * @returns The new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function setManualFocusState(\n input:\n SetManualFocusStateInput\n): FocusControlState {\n requireInputKeys(input, \"setManualFocusState\", [\"state\", \"stateId\", \"focus\", \"eventTimeSeconds\", \"commandId\"]);\n requireInputObject(input.state, \"state\");\n if (\n input.state.mode !==\n \"manual\"\n ) {\n throw new InvalidScientificInputError(\n \"setManualFocusState requires manual focus mode.\"\n );\n }\n if (input.state.locked) {\n throw new InvalidScientificInputError(\n \"Manual focus cannot change while focus lock is active.\"\n );\n }\n if (\n input.commandId !==\n undefined\n ) {\n requireScientificNonEmptyString(\n input.commandId,\n \"commandId\"\n );\n }\n const time =\n requireMonotonicEventTime(\n input.state,\n input.eventTimeSeconds\n );\n\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"manual\",\n input.focus,\n \"idle\",\n time,\n time\n );\n}\n\nfunction parseObservation(\n observation:\n FocusTargetObservation\n): FocusTargetObservation {\n const targetId =\n requireScientificNonEmptyString(\n observation.targetId,\n \"observation.targetId\"\n );\n const observedAtSeconds =\n requireScientificNonNegativeFinite(\n observation\n .observedAtSeconds,\n \"observation.observedAtSeconds\"\n );\n const focusAreaId =\n observation.focusAreaId ===\n undefined\n ? undefined\n : requireScientificNonEmptyString(\n observation.focusAreaId,\n \"observation.focusAreaId\"\n );\n\n if (\n observation.kind ===\n \"finite-surface\"\n ) {\n return {\n kind: \"finite-surface\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds,\n longitudinalDistanceM:\n requireScientificPositiveFinite(\n observation\n .longitudinalDistanceM,\n \"observation.longitudinalDistanceM\"\n )\n };\n }\n\n if (\n observation.kind ===\n \"infinity-environment\"\n ) {\n return {\n kind:\n \"infinity-environment\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds\n };\n }\n\n if (\n observation.kind ===\n \"unavailable\" &&\n TARGET_LOSS_REASONS.has(\n observation.reason\n )\n ) {\n return {\n kind: \"unavailable\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds,\n reason:\n observation.reason\n };\n }\n\n throw new InvalidConfigurationError(\n \"observation.kind/reason is invalid.\"\n );\n}\n\nfunction focusFromObservation(\n observation:\n Exclude<\n FocusTargetObservation,\n {\n kind: \"unavailable\";\n }\n >\n): FocusPlane {\n if (\n observation.kind ===\n \"infinity-environment\"\n ) {\n return {\n kind: \"infinity\"\n };\n }\n return {\n kind: \"finite\",\n distanceM:\n observation\n .longitudinalDistanceM\n };\n}\n\nfunction focusEquals(\n a: FocusPlane,\n b: FocusPlane\n): boolean {\n if (\n a.kind !== b.kind\n ) {\n return false;\n }\n if (\n a.kind === \"infinity\" ||\n b.kind === \"infinity\"\n ) {\n return true;\n }\n return (\n a.distanceM ===\n b.distanceM\n );\n}\n\nfunction stateFromResolvedTarget(\n input:\n ResolveFocusTargetObservationInput,\n observation:\n Exclude<\n FocusTargetObservation,\n {\n kind: \"unavailable\";\n }\n >\n): FocusTargetResolution {\n const focus =\n focusFromObservation(\n observation\n );\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n focus,\n \"acquired\",\n observation\n .observedAtSeconds,\n observation\n .observedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged:\n !focusEquals(\n input.state.focus,\n focus\n ),\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n}\n\nfunction unchangedResolution(\n input:\n ResolveFocusTargetObservationInput\n): FocusTargetResolution {\n return {\n state: input.state,\n targetObservationApplied:\n false,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n}\n\n/**\n * Applies one target observation (target available or lost) under the selected AF-S or AF-C\n * policy, and records whether the target was acquired or reacquired.\n *\n * The ideal actuator focuses on the observation as given. Target loss, reacquisition, focus\n * lock and release priority are separate state changes.\n * @param input - Current state, new state ID, control event and target observation.\n * @returns The resolution and new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveFocusTargetObservation(\n input:\n ResolveFocusTargetObservationInput\n): FocusTargetResolution {\n requireInputKeys(input, \"resolveFocusTargetObservation\", [\"state\", \"stateId\", \"event\", \"observation\"]);\n if (\n input.event !== \"acquire\" &&\n input.event !== \"update\"\n ) {\n throw new InvalidConfigurationError(\n 'Focus target event must be \"acquire\" or \"update\".'\n );\n }\n if (\n input.state.mode ===\n \"manual\"\n ) {\n throw new InvalidScientificInputError(\n \"Manual focus does not automatically consume autofocus target observations.\"\n );\n }\n\n const observation =\n parseObservation(\n input.observation\n );\n requireMonotonicEventTime(\n input.state,\n observation\n .observedAtSeconds\n );\n\n if (input.state.locked) {\n return unchangedResolution(\n input\n );\n }\n\n if (\n input.state.mode ===\n \"single-af\"\n ) {\n if (\n input.event ===\n \"update\" &&\n input.state\n .acquisitionState ===\n \"acquired\"\n ) {\n return unchangedResolution(\n input\n );\n }\n if (\n input.event !==\n \"acquire\"\n ) {\n throw new InvalidScientificInputError(\n \"Single AF requires an explicit acquire event before target updates.\"\n );\n }\n if (\n observation.kind ===\n \"unavailable\"\n ) {\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"single-af\",\n input.state.focus,\n \"target-lost\",\n observation\n .observedAtSeconds,\n input.state\n .focusResolvedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n }\n return stateFromResolvedTarget(\n input,\n observation\n );\n }\n\n if (\n input.event ===\n \"update\"\n ) {\n if (\n input.state\n .activeTargetId ===\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF requires an explicit acquire event before updates.\"\n );\n }\n if (\n observation.targetId !==\n input.state\n .activeTargetId\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF update targetId must match the active target; silent retargeting is not permitted.\"\n );\n }\n if (\n input.state\n .acquisitionState ===\n \"target-lost\"\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF target loss requires an explicit acquire event before reacquisition.\"\n );\n }\n }\n\n if (\n observation.kind ===\n \"unavailable\"\n ) {\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"continuous-af\",\n input.state.focus,\n \"target-lost\",\n observation\n .observedAtSeconds,\n input.state\n .focusResolvedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n }\n\n return stateFromResolvedTarget(\n input,\n observation\n );\n}\n\n/**\n * Locks or unlocks focus. The physical focus plane does not change.\n * @param input - Current state, new state ID, lock setting and event time.\n * @returns The new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function setFocusLock(\n input:\n SetFocusLockInput\n): FocusControlState {\n requireInputKeys(input, \"setFocusLock\", [\"state\", \"stateId\", \"locked\", \"eventTimeSeconds\"]);\n const time =\n requireMonotonicEventTime(\n input.state,\n input.eventTimeSeconds\n );\n\n if (input.locked) {\n if (input.state.locked) {\n return input.state;\n }\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n input.state.focus,\n \"locked\",\n time,\n input.state\n .focusResolvedAtSeconds,\n input.state\n .activeTargetId,\n input.state\n .activeFocusAreaId,\n {\n lockedAtSeconds:\n time,\n resumeAcquisitionState:\n input.state\n .acquisitionState as Exclude<\n FocusAcquisitionState,\n \"locked\"\n >\n }\n );\n }\n\n if (!input.state.locked) {\n return input.state;\n }\n const resume =\n input.state.lock\n ?.resumeAcquisitionState;\n if (resume === undefined) {\n throw new InvalidScientificInputError(\n \"Locked focus state is missing its resume acquisition state.\"\n );\n }\n\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n input.state.focus,\n resume,\n time,\n input.state\n .focusResolvedAtSeconds,\n input.state\n .activeTargetId,\n input.state\n .activeFocusAreaId\n );\n}\n\n/**\n * Reports whether the shutter may be released under the focus-priority or release-priority\n * policy. It does not operate the shutter.\n * @param input - Current state, profile and release priority.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessFocusReleaseGate(\n input:\n AssessFocusReleaseGateInput\n): FocusReleaseGateAssessment {\n requireInputKeys(input, \"assessFocusReleaseGate\", [\"state\", \"profile\", \"priority\"]);\n const profile =\n parseFocusControlProfile(\n input.profile\n );\n verifyStateProfile(\n input.state,\n profile\n );\n\n if (\n !profile\n .supportedReleasePriorities\n .includes(\n input.priority\n )\n ) {\n throw new InvalidScientificInputError(\n \"Selected focus/release priority is not supported by the profile.\"\n );\n }\n\n if (\n input.priority ===\n \"release-priority\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"release-priority-bypasses-focus-gate\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.priority ===\n \"balanced\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized:\n false,\n reason:\n \"balanced-policy-not-modeled\",\n requiresProfileSpecificBalancedPolicy:\n true,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state.mode ===\n \"manual\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"manual-focus-state\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state\n .acquisitionState ===\n \"locked\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"focus-locked\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state\n .acquisitionState ===\n \"acquired\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"focus-acquired\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n return {\n priority:\n input.priority,\n releaseAuthorized: false,\n reason:\n \"focus-not-acquired\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n}\n"]}
1
+ {"version":3,"file":"focus-control.js","sourceRoot":"","sources":["../../src/capture/focus-control.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Focus control: manual focus (MF), single autofocus (AF-S) and continuous autofocus (AF-C), focus\n * lock, and the focus-priority or release-priority check before the shutter fires.\n *\n * Focus is always a distance along the optical axis (a focus plane) or infinity, never a distance\n * on screen. The focus \"actuator\" is ideal: it focuses on the target observations it is given. It\n * does not model a real AF sensor, a focus-ring scale or how long focusing takes. Losing a target,\n * reacquiring it, locking focus and the release-priority decision are separate state changes.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseFocusPlane,\n type FocusPlane\n} from \"../optics/focus-state.js\";\nimport { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireTrimmedConfigString as requireScientificNonEmptyString, requireInputNonNegativeFinite as requireScientificNonNegativeFinite, requireInputPositiveFinite as requireScientificPositiveFinite, parseConfigLimitations as parseLimitations } from \"../core/record-validation.js\";\nimport { deepFreeze, cloneJson } from \"../core/shared-helpers.js\";\n\nexport const FOCUS_CONTROL_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\nexport const FOCUS_CONTROL_STATE_VERSION =\n \"0.1.0\" as const;\n\nexport type FocusControlMode =\n | \"manual\"\n | \"single-af\"\n | \"continuous-af\";\n\nexport type FocusAcquisitionState =\n | \"idle\"\n | \"acquiring\"\n | \"acquired\"\n | \"target-lost\"\n | \"locked\";\n\nexport type FocusReleasePriority =\n | \"focus-priority\"\n | \"release-priority\"\n | \"balanced\";\n\nexport type FocusTargetLossReason =\n | \"occluded\"\n | \"out-of-frame\"\n | \"identity-lost\"\n | \"outside-focus-area\";\n\nexport interface FocusControlProfile {\n schemaVersion:\n typeof FOCUS_CONTROL_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n scientificStatus: \"approximation\";\n supportedModes:\n readonly FocusControlMode[];\n actuator: {\n kind: \"ideal-instantaneous\";\n evidence:\n readonly EvidenceProvenance[];\n };\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\";\n supportedReleasePriorities:\n readonly FocusReleasePriority[];\n evidence:\n readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\ninterface FocusTargetObservationBase {\n targetId: string;\n focusAreaId?: string;\n observedAtSeconds: number;\n}\n\nexport type FocusTargetObservation =\n | (FocusTargetObservationBase & {\n kind: \"finite-surface\";\n /**\n * Distance along the camera's optical axis (the same convention as\n * FocusPlane), not the renderer's ray length to the point.\n */\n longitudinalDistanceM: number;\n })\n | (FocusTargetObservationBase & {\n kind:\n \"infinity-environment\";\n })\n | (FocusTargetObservationBase & {\n kind: \"unavailable\";\n reason:\n FocusTargetLossReason;\n });\n\n/**\n * Immutable focus-controller state for MF, AF-S or AF-C.\n *\n * The resolved focus is a finite distance along the optical axis in metres, or infinity.\n * Acquisition, loss, reacquisition and lock records explain why a target can be used. The\n * focus-release check is separate from shutter sequencing. The ideal actuator does not model\n * real AF accuracy or the physical time focusing takes.\n */\nexport interface FocusControlState {\n version:\n typeof FOCUS_CONTROL_STATE_VERSION;\n stateId: string;\n sourceProfile: {\n profileId: string;\n profileVersion: string;\n };\n profileLimitations:\n readonly string[];\n mode:\n FocusControlMode;\n acquisitionState:\n FocusAcquisitionState;\n focus:\n FocusPlane;\n activeTargetId?: string;\n activeFocusAreaId?: string;\n lastEventTimeSeconds: number;\n focusResolvedAtSeconds:\n number;\n locked: boolean;\n lock?: {\n lockedAtSeconds: number;\n resumeAcquisitionState:\n Exclude<\n FocusAcquisitionState,\n \"locked\"\n >;\n };\n actuatorModel:\n \"ideal-instantaneous\";\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\";\n exposureModified: false;\n meteringModified: false;\n whiteBalanceModified: false;\n subjectTrackingRecognitionModeled:\n false;\n rendererCoordinatesConsumed:\n false;\n limitations:\n readonly string[];\n}\n\nexport interface CreateFocusControlStateInput {\n stateId: string;\n profile:\n FocusControlProfile;\n mode:\n FocusControlMode;\n initialFocus:\n FocusPlane;\n initialTimeSeconds?: number;\n}\n\nexport interface SetManualFocusStateInput {\n state:\n FocusControlState;\n stateId: string;\n focus:\n FocusPlane;\n eventTimeSeconds: number;\n commandId?: string;\n}\n\nexport type FocusTargetControlEvent =\n | \"acquire\"\n | \"update\";\n\nexport interface ResolveFocusTargetObservationInput {\n state:\n FocusControlState;\n stateId: string;\n event:\n FocusTargetControlEvent;\n observation:\n FocusTargetObservation;\n}\n\nexport interface FocusTargetResolution {\n state:\n FocusControlState;\n targetObservationApplied:\n boolean;\n focusChanged: boolean;\n acquisitionEvent:\n FocusTargetControlEvent;\n actuatorModel:\n \"ideal-instantaneous\";\n silentRetargetingPerformed:\n false;\n}\n\nexport interface SetFocusLockInput {\n state:\n FocusControlState;\n stateId: string;\n locked: boolean;\n eventTimeSeconds: number;\n}\n\nexport interface AssessFocusReleaseGateInput {\n state:\n FocusControlState;\n profile:\n FocusControlProfile;\n priority:\n FocusReleasePriority;\n}\n\nexport interface FocusReleaseGateAssessment {\n priority:\n FocusReleasePriority;\n releaseAuthorized: boolean;\n reason:\n | \"manual-focus-state\"\n | \"focus-acquired\"\n | \"focus-locked\"\n | \"release-priority-bypasses-focus-gate\"\n | \"focus-not-acquired\"\n | \"balanced-policy-not-modeled\";\n requiresProfileSpecificBalancedPolicy:\n boolean;\n opticalFocusModified: false;\n exposureModified: false;\n}\n\nconst FOCUS_MODES =\n new Set<FocusControlMode>([\n \"manual\",\n \"single-af\",\n \"continuous-af\"\n ]);\n\nconst RELEASE_PRIORITIES =\n new Set<FocusReleasePriority>([\n \"focus-priority\",\n \"release-priority\",\n \"balanced\"\n ]);\n\nconst TARGET_LOSS_REASONS =\n new Set<FocusTargetLossReason>([\n \"occluded\",\n \"out-of-frame\",\n \"identity-lost\",\n \"outside-focus-area\"\n ]);\n\nfunction parseUniqueEnumList<T extends string>(\n value: unknown,\n path: string,\n allowed: ReadonlySet<T>\n): readonly T[] {\n if (\n !Array.isArray(value) ||\n value.length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-empty array.\"\n );\n }\n const parsed =\n value.map((entry, index) => {\n if (\n typeof entry !==\n \"string\" ||\n !allowed.has(entry as T)\n ) {\n throw new InvalidConfigurationError(\n path +\n \"[\" +\n index +\n \"] is invalid.\"\n );\n }\n return entry as T;\n });\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\n/**\n * Validates an untrusted focus-control profile and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Focus is a distance along the optical axis or infinity, never a distance on screen. The ideal\n * actuator focuses on the target observations it is given. It does not model a real AF sensor, a\n * focus-ring scale or focusing time.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The validated profile. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseFocusControlProfile(\n value: unknown\n): FocusControlProfile {\n const record =\n requireRecord(\n value,\n \"focusControlProfile\", [\"schemaVersion\", \"scientificStatus\", \"actuator\", \"continuousTargetLossPolicy\", \"profileId\", \"profileVersion\", \"supportedModes\", \"supportedReleasePriorities\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.schemaVersion must be \"' +\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.scientificStatus must be \"approximation\".'\n );\n }\n\n const actuator =\n requireRecord(\n record.actuator,\n \"focusControlProfile.actuator\", [\"kind\", \"evidence\"]\n );\n if (\n actuator.kind !==\n \"ideal-instantaneous\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.actuator.kind must be \"ideal-instantaneous\" in schema 0.1.0.'\n );\n }\n if (\n record\n .continuousTargetLossPolicy !==\n \"hold-last-focus-require-explicit-reacquisition\"\n ) {\n throw new InvalidConfigurationError(\n 'focusControlProfile.continuousTargetLossPolicy must be \"hold-last-focus-require-explicit-reacquisition\".'\n );\n }\n\n return {\n schemaVersion:\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"focusControlProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"focusControlProfile.profileVersion\"\n ),\n scientificStatus:\n \"approximation\",\n supportedModes:\n parseUniqueEnumList(\n record.supportedModes,\n \"focusControlProfile.supportedModes\",\n FOCUS_MODES\n ),\n actuator: {\n kind:\n \"ideal-instantaneous\",\n evidence:\n parseEvidenceList(\n actuator.evidence,\n \"focusControlProfile.actuator.evidence\"\n )\n },\n continuousTargetLossPolicy:\n \"hold-last-focus-require-explicit-reacquisition\",\n supportedReleasePriorities:\n parseUniqueEnumList(\n record\n .supportedReleasePriorities,\n \"focusControlProfile.supportedReleasePriorities\",\n RELEASE_PRIORITIES\n ),\n evidence:\n parseEvidenceList(\n record.evidence,\n \"focusControlProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"focusControlProfile.limitations\"\n )\n };\n}\n\nfunction immutableState(\n state:\n FocusControlState\n): FocusControlState {\n return deepFreeze(\n cloneJson(state)\n );\n}\n\nfunction stateBase(\n stateId: string,\n profile:\n FocusControlProfile,\n mode:\n FocusControlMode,\n focus:\n FocusPlane,\n acquisitionState:\n FocusAcquisitionState,\n eventTimeSeconds: number,\n focusResolvedAtSeconds:\n number,\n activeTargetId?:\n string,\n activeFocusAreaId?:\n string,\n lock?:\n FocusControlState[\"lock\"]\n): FocusControlState {\n return immutableState({\n version:\n FOCUS_CONTROL_STATE_VERSION,\n stateId:\n requireScientificNonEmptyString(\n stateId,\n \"stateId\"\n ),\n sourceProfile: {\n profileId:\n profile.profileId,\n profileVersion:\n profile.profileVersion\n },\n profileLimitations: [\n ...profile.limitations\n ],\n mode,\n acquisitionState,\n focus:\n parseFocusPlane(focus),\n ...(activeTargetId ===\n undefined\n ? {}\n : {\n activeTargetId\n }),\n ...(activeFocusAreaId ===\n undefined\n ? {}\n : {\n activeFocusAreaId\n }),\n lastEventTimeSeconds:\n eventTimeSeconds,\n focusResolvedAtSeconds,\n locked:\n acquisitionState ===\n \"locked\",\n ...(lock === undefined\n ? {}\n : {\n lock\n }),\n actuatorModel:\n \"ideal-instantaneous\",\n continuousTargetLossPolicy:\n profile\n .continuousTargetLossPolicy,\n exposureModified: false,\n meteringModified: false,\n whiteBalanceModified:\n false,\n subjectTrackingRecognitionModeled:\n false,\n rendererCoordinatesConsumed:\n false,\n limitations: [\n ...profile.limitations,\n \"Focus target observations use longitudinal camera-space/conjugate distance, not renderer ray length.\",\n \"Ideal instantaneous focus actuation is an approximation and does not represent commercial AF speed, error, hunting, recognition, or low-light behavior.\",\n \"Focus control does not modify exposure, metering, white balance, or scene illumination.\"\n ]\n });\n}\n\nfunction requireSupportedMode(\n profile:\n FocusControlProfile,\n mode:\n FocusControlMode\n): void {\n if (\n !profile.supportedModes\n .includes(mode)\n ) {\n throw new InvalidScientificInputError(\n \"Selected focus-control mode is not supported by the profile.\"\n );\n }\n}\n\nfunction requireMonotonicEventTime(\n current:\n FocusControlState,\n eventTimeSeconds: number\n): number {\n const parsed =\n requireScientificNonNegativeFinite(\n eventTimeSeconds,\n \"eventTimeSeconds\"\n );\n if (\n parsed <\n current.lastEventTimeSeconds\n ) {\n throw new InvalidScientificInputError(\n \"Focus-control event time must not move backward.\"\n );\n }\n return parsed;\n}\n\nfunction verifyStateProfile(\n state:\n FocusControlState,\n profile:\n FocusControlProfile\n): void {\n if (\n state.sourceProfile\n .profileId !==\n profile.profileId ||\n state.sourceProfile\n .profileVersion !==\n profile.profileVersion\n ) {\n throw new InvalidScientificInputError(\n \"Focus-control state/profile identity does not match.\"\n );\n }\n}\n\n/**\n * Creates the initial ideal focus-controller state from a profile, a focus plane and a public\n * state ID.\n * @param input - New state ID, profile, focus mode, initial focus plane and optional start time.\n * @returns The initial state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function createFocusControlState(\n input:\n CreateFocusControlStateInput\n): FocusControlState {\n requireInputKeys(input, \"createFocusControlState\", [\"stateId\", \"profile\", \"mode\", \"initialFocus\", \"initialTimeSeconds\"]);\n const profile =\n parseFocusControlProfile(\n input.profile\n );\n requireSupportedMode(\n profile,\n input.mode\n );\n const time =\n input.initialTimeSeconds ===\n undefined\n ? 0\n : requireScientificNonNegativeFinite(\n input.initialTimeSeconds,\n \"initialTimeSeconds\"\n );\n\n return stateBase(\n input.stateId,\n profile,\n input.mode,\n input.initialFocus,\n \"idle\",\n time,\n time\n );\n}\n\nfunction stateProfileView(\n state:\n FocusControlState\n): FocusControlProfile {\n return {\n schemaVersion:\n FOCUS_CONTROL_PROFILE_SCHEMA_VERSION,\n profileId:\n state.sourceProfile\n .profileId,\n profileVersion:\n state.sourceProfile\n .profileVersion,\n scientificStatus:\n \"approximation\",\n supportedModes: [\n state.mode\n ],\n actuator: {\n kind:\n \"ideal-instantaneous\",\n evidence: []\n },\n continuousTargetLossPolicy:\n state\n .continuousTargetLossPolicy,\n supportedReleasePriorities: [\n \"focus-priority\",\n \"release-priority\",\n \"balanced\"\n ],\n evidence: [],\n limitations:\n state.profileLimitations\n };\n}\n\n/**\n * Returns the state after manually focusing on the given finite or infinity plane. No lens\n * actuator or focus-ring conversion is modelled.\n * @param input - Current state, new state ID, new focus plane and event time.\n * @returns The new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function setManualFocusState(\n input:\n SetManualFocusStateInput\n): FocusControlState {\n requireInputKeys(input, \"setManualFocusState\", [\"state\", \"stateId\", \"focus\", \"eventTimeSeconds\", \"commandId\"]);\n requireInputObject(input.state, \"state\");\n if (\n input.state.mode !==\n \"manual\"\n ) {\n throw new InvalidScientificInputError(\n \"setManualFocusState requires manual focus mode.\"\n );\n }\n if (input.state.locked) {\n throw new InvalidScientificInputError(\n \"Manual focus cannot change while focus lock is active.\"\n );\n }\n if (\n input.commandId !==\n undefined\n ) {\n requireScientificNonEmptyString(\n input.commandId,\n \"commandId\"\n );\n }\n const time =\n requireMonotonicEventTime(\n input.state,\n input.eventTimeSeconds\n );\n\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"manual\",\n input.focus,\n \"idle\",\n time,\n time\n );\n}\n\nfunction parseObservation(\n observation:\n FocusTargetObservation\n): FocusTargetObservation {\n const targetId =\n requireScientificNonEmptyString(\n observation.targetId,\n \"observation.targetId\"\n );\n const observedAtSeconds =\n requireScientificNonNegativeFinite(\n observation\n .observedAtSeconds,\n \"observation.observedAtSeconds\"\n );\n const focusAreaId =\n observation.focusAreaId ===\n undefined\n ? undefined\n : requireScientificNonEmptyString(\n observation.focusAreaId,\n \"observation.focusAreaId\"\n );\n\n if (\n observation.kind ===\n \"finite-surface\"\n ) {\n return {\n kind: \"finite-surface\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds,\n longitudinalDistanceM:\n requireScientificPositiveFinite(\n observation\n .longitudinalDistanceM,\n \"observation.longitudinalDistanceM\"\n )\n };\n }\n\n if (\n observation.kind ===\n \"infinity-environment\"\n ) {\n return {\n kind:\n \"infinity-environment\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds\n };\n }\n\n if (\n observation.kind ===\n \"unavailable\" &&\n TARGET_LOSS_REASONS.has(\n observation.reason\n )\n ) {\n return {\n kind: \"unavailable\",\n targetId,\n ...(focusAreaId ===\n undefined\n ? {}\n : {\n focusAreaId\n }),\n observedAtSeconds,\n reason:\n observation.reason\n };\n }\n\n throw new InvalidConfigurationError(\n \"observation.kind/reason is invalid.\"\n );\n}\n\nfunction focusFromObservation(\n observation:\n Exclude<\n FocusTargetObservation,\n {\n kind: \"unavailable\";\n }\n >\n): FocusPlane {\n if (\n observation.kind ===\n \"infinity-environment\"\n ) {\n return {\n kind: \"infinity\"\n };\n }\n return {\n kind: \"finite\",\n distanceM:\n observation\n .longitudinalDistanceM\n };\n}\n\nfunction focusEquals(\n a: FocusPlane,\n b: FocusPlane\n): boolean {\n if (\n a.kind !== b.kind\n ) {\n return false;\n }\n if (\n a.kind === \"infinity\" ||\n b.kind === \"infinity\"\n ) {\n return true;\n }\n return (\n a.distanceM ===\n b.distanceM\n );\n}\n\nfunction stateFromResolvedTarget(\n input:\n ResolveFocusTargetObservationInput,\n observation:\n Exclude<\n FocusTargetObservation,\n {\n kind: \"unavailable\";\n }\n >\n): FocusTargetResolution {\n const focus =\n focusFromObservation(\n observation\n );\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n focus,\n \"acquired\",\n observation\n .observedAtSeconds,\n observation\n .observedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged:\n !focusEquals(\n input.state.focus,\n focus\n ),\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n}\n\nfunction unchangedResolution(\n input:\n ResolveFocusTargetObservationInput\n): FocusTargetResolution {\n return {\n state: input.state,\n targetObservationApplied:\n false,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n}\n\n/**\n * Applies one target observation (target available or lost) under the selected AF-S or AF-C\n * policy, and records whether the target was acquired or reacquired.\n *\n * The ideal actuator focuses on the observation as given. Target loss, reacquisition, focus\n * lock and release priority are separate state changes.\n * @param input - Current state, new state ID, control event and target observation.\n * @returns The resolution and new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveFocusTargetObservation(\n input:\n ResolveFocusTargetObservationInput\n): FocusTargetResolution {\n requireInputKeys(input, \"resolveFocusTargetObservation\", [\"state\", \"stateId\", \"event\", \"observation\"]);\n if (\n input.event !== \"acquire\" &&\n input.event !== \"update\"\n ) {\n throw new InvalidConfigurationError(\n 'Focus target event must be \"acquire\" or \"update\".'\n );\n }\n if (\n input.state.mode ===\n \"manual\"\n ) {\n throw new InvalidScientificInputError(\n \"Manual focus does not automatically consume autofocus target observations.\"\n );\n }\n\n const observation =\n parseObservation(\n input.observation\n );\n requireMonotonicEventTime(\n input.state,\n observation\n .observedAtSeconds\n );\n\n if (input.state.locked) {\n return unchangedResolution(\n input\n );\n }\n\n if (\n input.state.mode ===\n \"single-af\"\n ) {\n if (\n input.event ===\n \"update\" &&\n input.state\n .acquisitionState ===\n \"acquired\"\n ) {\n return unchangedResolution(\n input\n );\n }\n if (\n input.event !==\n \"acquire\"\n ) {\n throw new InvalidScientificInputError(\n \"Single AF requires an explicit acquire event before target updates.\"\n );\n }\n if (\n observation.kind ===\n \"unavailable\"\n ) {\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"single-af\",\n input.state.focus,\n \"target-lost\",\n observation\n .observedAtSeconds,\n input.state\n .focusResolvedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n }\n return stateFromResolvedTarget(\n input,\n observation\n );\n }\n\n if (\n input.event ===\n \"update\"\n ) {\n if (\n input.state\n .activeTargetId ===\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF requires an explicit acquire event before updates.\"\n );\n }\n if (\n observation.targetId !==\n input.state\n .activeTargetId\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF update targetId must match the active target; silent retargeting is not permitted.\"\n );\n }\n if (\n input.state\n .acquisitionState ===\n \"target-lost\"\n ) {\n throw new InvalidScientificInputError(\n \"Continuous AF target loss requires an explicit acquire event before reacquisition.\"\n );\n }\n }\n\n if (\n observation.kind ===\n \"unavailable\"\n ) {\n const state =\n stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n \"continuous-af\",\n input.state.focus,\n \"target-lost\",\n observation\n .observedAtSeconds,\n input.state\n .focusResolvedAtSeconds,\n observation.targetId,\n observation.focusAreaId\n );\n return {\n state,\n targetObservationApplied:\n true,\n focusChanged: false,\n acquisitionEvent:\n input.event,\n actuatorModel:\n \"ideal-instantaneous\",\n silentRetargetingPerformed:\n false\n };\n }\n\n return stateFromResolvedTarget(\n input,\n observation\n );\n}\n\n/**\n * Locks or unlocks focus. The physical focus plane does not change.\n * @param input - Current state, new state ID, lock setting and event time.\n * @returns The new state. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function setFocusLock(\n input:\n SetFocusLockInput\n): FocusControlState {\n requireInputKeys(input, \"setFocusLock\", [\"state\", \"stateId\", \"locked\", \"eventTimeSeconds\"]);\n const time =\n requireMonotonicEventTime(\n input.state,\n input.eventTimeSeconds\n );\n\n if (input.locked) {\n if (input.state.locked) {\n return input.state;\n }\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n input.state.focus,\n \"locked\",\n time,\n input.state\n .focusResolvedAtSeconds,\n input.state\n .activeTargetId,\n input.state\n .activeFocusAreaId,\n {\n lockedAtSeconds:\n time,\n resumeAcquisitionState:\n input.state\n .acquisitionState as Exclude<\n FocusAcquisitionState,\n \"locked\"\n >\n }\n );\n }\n\n if (!input.state.locked) {\n return input.state;\n }\n const resume =\n input.state.lock\n ?.resumeAcquisitionState;\n if (resume === undefined) {\n throw new InvalidScientificInputError(\n \"Locked focus state is missing its resume acquisition state.\"\n );\n }\n\n return stateBase(\n input.stateId,\n stateProfileView(\n input.state\n ),\n input.state.mode,\n input.state.focus,\n resume,\n time,\n input.state\n .focusResolvedAtSeconds,\n input.state\n .activeTargetId,\n input.state\n .activeFocusAreaId\n );\n}\n\n/**\n * Reports whether the shutter may be released under the focus-priority or release-priority\n * policy. It does not operate the shutter.\n * @param input - Current state, profile and release priority.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessFocusReleaseGate(\n input:\n AssessFocusReleaseGateInput\n): FocusReleaseGateAssessment {\n requireInputKeys(input, \"assessFocusReleaseGate\", [\"state\", \"profile\", \"priority\"]);\n const profile =\n parseFocusControlProfile(\n input.profile\n );\n verifyStateProfile(\n input.state,\n profile\n );\n\n if (\n !profile\n .supportedReleasePriorities\n .includes(\n input.priority\n )\n ) {\n throw new InvalidScientificInputError(\n \"Selected focus/release priority is not supported by the profile.\"\n );\n }\n\n if (\n input.priority ===\n \"release-priority\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"release-priority-bypasses-focus-gate\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.priority ===\n \"balanced\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized:\n false,\n reason:\n \"balanced-policy-not-modeled\",\n requiresProfileSpecificBalancedPolicy:\n true,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state.mode ===\n \"manual\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"manual-focus-state\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state\n .acquisitionState ===\n \"locked\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"focus-locked\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n if (\n input.state\n .acquisitionState ===\n \"acquired\"\n ) {\n return {\n priority:\n input.priority,\n releaseAuthorized: true,\n reason:\n \"focus-acquired\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n }\n\n return {\n priority:\n input.priority,\n releaseAuthorized: false,\n reason:\n \"focus-not-acquired\",\n requiresProfileSpecificBalancedPolicy:\n false,\n opticalFocusModified:\n false,\n exposureModified:\n false\n };\n}\n"]}
@@ -55,6 +55,7 @@ export declare const nativeEnvironmentLeanStatistics: {
55
55
  keySites: number;
56
56
  };
57
57
  export declare function setNativeEnvironmentLeanExecutionEnabled(enabled: boolean): void;
58
+ export declare function isPlainRecord(value: object): boolean;
58
59
  interface SpatialNode {
59
60
  xPhase: number;
60
61
  yPhase: number;
@@ -33,7 +33,7 @@ let leanDisabled = false;
33
33
  /** Internal counters of lean work (tests assert the lean path actually runs). Not exported from the package. */
34
34
  export const nativeEnvironmentLeanStatistics = { plannedSites: 0, executedSites: 0, sensorSites: 0, psfFallbacks: 0, keySites: 0 };
35
35
  export function setNativeEnvironmentLeanExecutionEnabled(enabled) { leanDisabled = !enabled; }
36
- function isPlainRecord(value) {
36
+ export function isPlainRecord(value) {
37
37
  const prototype = Object.getPrototypeOf(value);
38
38
  return prototype === Object.prototype || prototype === null;
39
39
  }