@photivra/engine 2.17.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 (288) hide show
  1. package/CHANGELOG.md +116 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/camera/sensor-equivalence.d.ts +156 -0
  6. package/dist/camera/sensor-equivalence.js +208 -0
  7. package/dist/camera/sensor-equivalence.js.map +1 -0
  8. package/dist/capture/focus-control.js +2 -75
  9. package/dist/capture/focus-control.js.map +1 -1
  10. package/dist/capture/native-environment-lean.d.ts +1 -0
  11. package/dist/capture/native-environment-lean.js +1 -1
  12. package/dist/capture/native-environment-lean.js.map +1 -1
  13. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  14. package/dist/capture/native-environment-optical-stage.js +2 -5
  15. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  16. package/dist/capture/native-environment-raw.js +1 -5
  17. package/dist/capture/native-environment-raw.js.map +1 -1
  18. package/dist/capture/native-environment-tabulated.d.ts +5 -0
  19. package/dist/capture/native-environment-tabulated.js +1 -1
  20. package/dist/capture/native-environment-tabulated.js.map +1 -1
  21. package/dist/capture/release-sequence.js +2 -47
  22. package/dist/capture/release-sequence.js.map +1 -1
  23. package/dist/color/white-balance.d.ts +2 -0
  24. package/dist/color/white-balance.js +3 -56
  25. package/dist/color/white-balance.js.map +1 -1
  26. package/dist/composition/image-formation-plan.d.ts +1 -0
  27. package/dist/composition/image-formation-plan.js +3 -49
  28. package/dist/composition/image-formation-plan.js.map +1 -1
  29. package/dist/core/evidence-provenance.js +1 -16
  30. package/dist/core/evidence-provenance.js.map +1 -1
  31. package/dist/core/record-validation.d.ts +54 -0
  32. package/dist/core/record-validation.js +176 -0
  33. package/dist/core/record-validation.js.map +1 -1
  34. package/dist/core/scientific-assurance.js +1 -18
  35. package/dist/core/scientific-assurance.js.map +1 -1
  36. package/dist/core/shared-helpers.d.ts +33 -0
  37. package/dist/core/shared-helpers.js +34 -0
  38. package/dist/core/shared-helpers.js.map +1 -0
  39. package/dist/core/spectral-composition.js +1 -36
  40. package/dist/core/spectral-composition.js.map +1 -1
  41. package/dist/core/spectral.js +1 -19
  42. package/dist/core/spectral.js.map +1 -1
  43. package/dist/core/version.d.ts +1 -1
  44. package/dist/core/version.js +1 -1
  45. package/dist/core/version.js.map +1 -1
  46. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  47. package/dist/equipment/exposure-capabilities.js +2 -36
  48. package/dist/equipment/exposure-capabilities.js.map +1 -1
  49. package/dist/equipment/ideal-lens.d.ts +25 -0
  50. package/dist/equipment/ideal-lens.js +21 -0
  51. package/dist/equipment/ideal-lens.js.map +1 -0
  52. package/dist/equipment/iso-capabilities.js +2 -49
  53. package/dist/equipment/iso-capabilities.js.map +1 -1
  54. package/dist/equipment/metering-capabilities.js +1 -26
  55. package/dist/equipment/metering-capabilities.js.map +1 -1
  56. package/dist/equipment/release-capabilities.js +1 -53
  57. package/dist/equipment/release-capabilities.js.map +1 -1
  58. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  59. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  60. package/dist/exposure/exposure-duration-control.js +1 -16
  61. package/dist/exposure/exposure-duration-control.js.map +1 -1
  62. package/dist/exposure/exposure-mode-resolver.js +1 -17
  63. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  64. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  65. package/dist/exposure/filtered-exposure-time.js +129 -0
  66. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  67. package/dist/exposure/flash-ttl.d.ts +1 -0
  68. package/dist/exposure/flash-ttl.js +1 -1
  69. package/dist/exposure/flash-ttl.js.map +1 -1
  70. package/dist/exposure/flash.js +1 -49
  71. package/dist/exposure/flash.js.map +1 -1
  72. package/dist/exposure/metering-scene-radiance.js +1 -25
  73. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  74. package/dist/exposure/metering-target.js +1 -30
  75. package/dist/exposure/metering-target.js.map +1 -1
  76. package/dist/exposure/metering-temporal.js +1 -8
  77. package/dist/exposure/metering-temporal.js.map +1 -1
  78. package/dist/exposure/metering.js +1 -72
  79. package/dist/exposure/metering.js.map +1 -1
  80. package/dist/exposure/program-line.js +1 -45
  81. package/dist/exposure/program-line.js.map +1 -1
  82. package/dist/facade/catalogue-lens-signal.d.ts +94 -0
  83. package/dist/facade/catalogue-lens-signal.js +362 -0
  84. package/dist/facade/catalogue-lens-signal.js.map +1 -0
  85. package/dist/facade/defocus-image-space-provider.js +5 -2
  86. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  87. package/dist/facade/develop-joined-parts.d.ts +29 -0
  88. package/dist/facade/develop-joined-parts.js +33 -0
  89. package/dist/facade/develop-joined-parts.js.map +1 -0
  90. package/dist/facade/display-view-lens-signal.d.ts +32 -1
  91. package/dist/facade/display-view-lens-signal.js +85 -10
  92. package/dist/facade/display-view-lens-signal.js.map +1 -1
  93. package/dist/facade/expected-signal-setup.d.ts +8 -0
  94. package/dist/facade/expected-signal-setup.js +7 -0
  95. package/dist/facade/expected-signal-setup.js.map +1 -1
  96. package/dist/facade/expected-signal.d.ts +5 -0
  97. package/dist/facade/expected-signal.js +5 -1
  98. package/dist/facade/expected-signal.js.map +1 -1
  99. package/dist/facade/generic-lens-capture.d.ts +5 -2
  100. package/dist/facade/generic-lens-capture.js +5 -2
  101. package/dist/facade/generic-lens-capture.js.map +1 -1
  102. package/dist/facade/simulate-capture.d.ts +67 -8
  103. package/dist/facade/simulate-capture.js +262 -126
  104. package/dist/facade/simulate-capture.js.map +1 -1
  105. package/dist/facade/simulate-display-view.js +1 -1
  106. package/dist/facade/simulate-display-view.js.map +1 -1
  107. package/dist/gpu/chart-composite-gpu.js +1 -3
  108. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  109. package/dist/gpu/chart-radiance-gpu.js +1 -5
  110. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  111. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  112. package/dist/gpu/display-view-quality-gpu.js +4 -8
  113. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  114. package/dist/gpu/display-view-speed-gpu.js +3 -25
  115. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  116. package/dist/gpu/sensor-stage-gpu.js +1 -5
  117. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  118. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  119. package/dist/gpu/webgpu-runtime.js +3 -0
  120. package/dist/gpu/webgpu-runtime.js.map +1 -1
  121. package/dist/index.d.ts +6 -1
  122. package/dist/index.js +5 -0
  123. package/dist/index.js.map +1 -1
  124. package/dist/motion/camera-rotation.js +1 -6
  125. package/dist/motion/camera-rotation.js.map +1 -1
  126. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  127. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  128. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  129. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  130. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  131. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  132. package/dist/motion/capture-rotation-trajectory.js +2 -17
  133. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  134. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  135. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  136. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  137. package/dist/motion/extended-object-projection.js +5 -40
  138. package/dist/motion/extended-object-projection.js.map +1 -1
  139. package/dist/optics/atmospheric-haze.js +1 -7
  140. package/dist/optics/atmospheric-haze.js.map +1 -1
  141. package/dist/optics/brown-conrady-distortion.js +1 -7
  142. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  143. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  144. package/dist/optics/complex-pupil-psf.js +2 -84
  145. package/dist/optics/complex-pupil-psf.js.map +1 -1
  146. package/dist/optics/focus-state.js +1 -11
  147. package/dist/optics/focus-state.js.map +1 -1
  148. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  149. package/dist/optics/front-of-lens-filter.js +3 -65
  150. package/dist/optics/front-of-lens-filter.js.map +1 -1
  151. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  152. package/dist/optics/generic-nd-filter-presets.js +48 -0
  153. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  154. package/dist/optics/illumination-vignetting.js +1 -6
  155. package/dist/optics/illumination-vignetting.js.map +1 -1
  156. package/dist/optics/lens-psf-profile.d.ts +4 -1
  157. package/dist/optics/lens-psf-profile.js +12 -111
  158. package/dist/optics/lens-psf-profile.js.map +1 -1
  159. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  160. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  161. package/dist/optics/pupil-vignetting.js +1 -7
  162. package/dist/optics/pupil-vignetting.js.map +1 -1
  163. package/dist/optics/radial-distortion.js +1 -6
  164. package/dist/optics/radial-distortion.js.map +1 -1
  165. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  166. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  167. package/dist/optics/stray-light.d.ts +1 -0
  168. package/dist/optics/stray-light.js +1 -1
  169. package/dist/optics/stray-light.js.map +1 -1
  170. package/dist/optics/zernike-wavefront.js +1 -7
  171. package/dist/optics/zernike-wavefront.js.map +1 -1
  172. package/dist/output/capture-geometry.js +1 -8
  173. package/dist/output/capture-geometry.js.map +1 -1
  174. package/dist/output/developed-lens-corrections.d.ts +62 -0
  175. package/dist/output/developed-lens-corrections.js +135 -0
  176. package/dist/output/developed-lens-corrections.js.map +1 -0
  177. package/dist/output/generic-lens-corrections.d.ts +2 -6
  178. package/dist/output/generic-lens-corrections.js +3 -6
  179. package/dist/output/generic-lens-corrections.js.map +1 -1
  180. package/dist/output/geometric-transforms.js +1 -4
  181. package/dist/output/geometric-transforms.js.map +1 -1
  182. package/dist/output/native-capture-sdr.d.ts +2 -2
  183. package/dist/output/native-capture-sdr.js +3 -2
  184. package/dist/output/native-capture-sdr.js.map +1 -1
  185. package/dist/processing/development-detail.d.ts +29 -0
  186. package/dist/processing/development-detail.js +128 -107
  187. package/dist/processing/development-detail.js.map +1 -1
  188. package/dist/schema/illumination-temporal.js +3 -72
  189. package/dist/schema/illumination-temporal.js.map +1 -1
  190. package/dist/schema/illumination.d.ts +1 -0
  191. package/dist/schema/illumination.js +2 -65
  192. package/dist/schema/illumination.js.map +1 -1
  193. package/dist/schema/scene-radiance-temporal.js +2 -14
  194. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  195. package/dist/schema/scene-radiance.js +2 -60
  196. package/dist/schema/scene-radiance.js.map +1 -1
  197. package/dist/schema/validation.d.ts +2 -0
  198. package/dist/schema/validation.js +2 -17
  199. package/dist/schema/validation.js.map +1 -1
  200. package/dist/sensor/accumulated-charge.js +2 -59
  201. package/dist/sensor/accumulated-charge.js.map +1 -1
  202. package/dist/sensor/architecture.js +1 -16
  203. package/dist/sensor/architecture.js.map +1 -1
  204. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  205. package/dist/sensor/camera-saturation-capacity.js +3 -55
  206. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  207. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  208. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  209. package/dist/sensor/capture-mode-timing.js +1 -39
  210. package/dist/sensor/capture-mode-timing.js.map +1 -1
  211. package/dist/sensor/capture-mode.js +1 -23
  212. package/dist/sensor/capture-mode.js.map +1 -1
  213. package/dist/sensor/color-sampling.js +1 -26
  214. package/dist/sensor/color-sampling.js.map +1 -1
  215. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  216. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  217. package/dist/sensor/dark-current.js +2 -45
  218. package/dist/sensor/dark-current.js.map +1 -1
  219. package/dist/sensor/eqe-electron-rate.js +2 -35
  220. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  221. package/dist/sensor/exposure-window.d.ts +5 -0
  222. package/dist/sensor/exposure-window.js +7 -22
  223. package/dist/sensor/exposure-window.js.map +1 -1
  224. package/dist/sensor/iso-gain.js +1 -7
  225. package/dist/sensor/iso-gain.js.map +1 -1
  226. package/dist/sensor/iso-signal-chain.js +1 -43
  227. package/dist/sensor/iso-signal-chain.js.map +1 -1
  228. package/dist/sensor/optical-stack.js +1 -25
  229. package/dist/sensor/optical-stack.js.map +1 -1
  230. package/dist/sensor/photon-energy.js +1 -26
  231. package/dist/sensor/photon-energy.js.map +1 -1
  232. package/dist/sensor/physical-charge-capacity.js +3 -77
  233. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  234. package/dist/sensor/radiometry-readiness.js +2 -23
  235. package/dist/sensor/radiometry-readiness.js.map +1 -1
  236. package/dist/sensor/raw-readout.d.ts +3 -0
  237. package/dist/sensor/raw-readout.js +4 -66
  238. package/dist/sensor/raw-readout.js.map +1 -1
  239. package/dist/sensor/raw-reconstruction.js +2 -64
  240. package/dist/sensor/raw-reconstruction.js.map +1 -1
  241. package/dist/sensor/readout-exposure-linkage.js +1 -9
  242. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  243. package/dist/sensor/readout-timing.js +3 -57
  244. package/dist/sensor/readout-timing.js.map +1 -1
  245. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  246. package/dist/sensor/response-application-compatibility.js +3 -29
  247. package/dist/sensor/response-application-compatibility.js.map +1 -1
  248. package/dist/sensor/response-operating-range.d.ts +1 -0
  249. package/dist/sensor/response-operating-range.js +3 -79
  250. package/dist/sensor/response-operating-range.js.map +1 -1
  251. package/dist/sensor/responsivity-photocurrent.js +4 -108
  252. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  253. package/dist/sensor/sampling-aperture.js +1 -25
  254. package/dist/sensor/sampling-aperture.js.map +1 -1
  255. package/dist/sensor/spatial-sample-reduction.js +2 -19
  256. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  257. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  258. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  259. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  260. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  261. package/dist/sensor/spectral-coverage.d.ts +10 -1
  262. package/dist/sensor/spectral-coverage.js +1 -1
  263. package/dist/sensor/spectral-coverage.js.map +1 -1
  264. package/dist/sensor/spectral-quadrature.js +2 -35
  265. package/dist/sensor/spectral-quadrature.js.map +1 -1
  266. package/dist/sensor/spectral-response.js +1 -47
  267. package/dist/sensor/spectral-response.js.map +1 -1
  268. package/dist/stabilization/camera-shake.js +1 -6
  269. package/dist/stabilization/camera-shake.js.map +1 -1
  270. package/dist/stabilization/system.js +2 -80
  271. package/dist/stabilization/system.js.map +1 -1
  272. package/docs/AGENT_GUIDE.md +4 -0
  273. package/docs/API_REFERENCE.md +69 -2
  274. package/docs/QUICK_START.md +136 -6
  275. package/docs/USAGE.md +89 -3
  276. package/docs/api/camera-sensor-equivalence.md +209 -0
  277. package/docs/api/core-version.md +1 -1
  278. package/docs/api/equipment-ideal-lens.md +26 -0
  279. package/docs/api/exports.json +118 -3
  280. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  281. package/docs/api/facade-develop-joined-parts.md +32 -0
  282. package/docs/api/facade-simulate-capture.md +62 -8
  283. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  284. package/docs/api/optics-lens-psf-profile.md +3 -1
  285. package/docs/api/output-developed-lens-corrections.md +12 -0
  286. package/docs/api/output-generic-lens-corrections.md +0 -9
  287. package/llms.txt +3 -0
  288. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensor-equivalence.js","sourceRoot":"","sources":["../../src/camera/sensor-equivalence.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Format equivalence for teaching: what a lens of focal length f at f-number N on one sensor\n * corresponds to on a reference format (36 × 24 mm by default), and how large a blur circle is\n * as a share of the frame or of a print.\n *\n * With the diagonal crop factor k = referenceDiagonal / sensorDiagonal:\n * - equivalent focal length k·f (same diagonal angle of view; exact by the diagonal convention);\n * - equivalent f-number k·N for depth of field, background blur and total light (same framing,\n * same output size). It keeps the entrance-pupil diameter f/N unchanged. It is an\n * approximation: in the thin-lens defocus-circle equation it is exact only when the focus\n * distance is much greater than the focal length;\n * - the f-number for exposure stays N: image illuminance depends on N, not on the sensor size;\n * - sensor area ratio and the total-light difference in stops at the same f-number and shutter.\n *\n * Sources (free): thin-lens defocus-circle diameter f²|s − d|/(N d (s − f)) and f-number N = f/D,\n * Greivenkamp, OPTI-502 course notes, sec. 12 (Objectives),\n * https://wp.optics.arizona.edu/jgreivenkamp/wp-content/uploads/sites/11/2019/08/502-12-Objectives.pdf;\n * image illuminance proportional to 1/N², sec. 11 (Radiative transfer),\n * https://wp.optics.arizona.edu/jgreivenkamp/wp-content/uploads/sites/11/2019/08/502-11-Radiative-Transfer.pdf.\n * @see docs/PHYSICS_FOUNDATION.md \"Sensor equivalence\" for the derivation, the exact and\n * approximated quantities, and support limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError, requirePositiveFinite } from \"../core/validation.js\";\nimport { portableHypot, portableLog2 } from \"../core/portable-math.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { calculateImagingAreaMetrics, type SensorImagingArea } from \"../sensor/sensor-geometry.js\";\n\n/** The default equivalence reference: the 36 × 24 mm frame. */\nconst REFERENCE_FULL_FRAME: Readonly<SensorImagingArea> = Object.freeze({ widthMm: 36, heightMm: 24 });\n\n/** Whether a value is exact under its stated conditions or an approximation. */\nexport type EquivalenceExactness = \"exact\" | \"approximation\";\n\n/** How one equivalence quantity was obtained, and under which conditions it holds. */\nexport interface EquivalenceValueBasis {\n /** `\"exact\"` under the stated conditions, or `\"approximation\"`. */\n kind: EquivalenceExactness;\n /** Plain statement of the definition and its conditions. */\n statement: string;\n}\n\nexport interface CalculateSensorEquivalenceInput {\n /** Physical active imaging area of the sensor the lens is used on. */\n sensor: SensorImagingArea;\n /** Physical focal length of the lens in millimetres. */\n focalLengthMm: number;\n /** f-number set on the lens (N = focal length / entrance-pupil diameter). */\n fNumber: number;\n /** Format to compare against. Defaults to the 36 × 24 mm frame. */\n reference?: SensorImagingArea;\n}\n\n/** Angles of view of an ideal rectilinear lens focused at infinity, in degrees. */\nexport interface SensorEquivalenceFieldOfView {\n horizontalDegrees: number;\n verticalDegrees: number;\n diagonalDegrees: number;\n}\n\n/** Quantities of a sensor equivalence result that carry an exactness statement. */\nexport type SensorEquivalenceQuantity =\n | \"cropFactor\"\n | \"equivalentFocalLengthMm\"\n | \"equivalentFNumber\"\n | \"exposureFNumber\"\n | \"entrancePupilDiameterMm\"\n | \"areaRatio\"\n | \"totalLightDifferenceStops\"\n | \"fieldOfView\";\n\nexport interface SensorEquivalence {\n /** Physical focal length supplied by the caller, in millimetres. */\n focalLengthMm: number;\n /** f-number supplied by the caller. */\n fNumber: number;\n /** Sensor active imaging area used. */\n sensor: SensorImagingArea;\n /** Reference format used (36 × 24 mm unless the caller passed one). */\n reference: SensorImagingArea;\n /** Sensor diagonal in millimetres. */\n sensorDiagonalMm: number;\n /** Reference diagonal in millimetres. */\n referenceDiagonalMm: number;\n /** Diagonal crop factor: reference diagonal / sensor diagonal. Greater than 1 for a smaller sensor. */\n cropFactor: number;\n /** Focal length on the reference format with the same diagonal angle of view: cropFactor × focalLengthMm. */\n equivalentFocalLengthMm: number;\n /**\n * f-number on the reference format that gives the same depth of field, background blur and total\n * light for the same framing and output size: cropFactor × fNumber. Not an exposure setting.\n */\n equivalentFNumber: number;\n /** f-number that sets exposure (image brightness): the f-number on the lens, unchanged by sensor size. */\n exposureFNumber: number;\n /** Entrance-pupil diameter f/N in millimetres; the equivalent lens has the same diameter. */\n entrancePupilDiameterMm: number;\n /** Sensor area / reference area. Less than 1 for a smaller sensor. */\n areaRatio: number;\n /**\n * log2(areaRatio): total light collected by the sensor relative to the reference at the same\n * f-number, shutter time and scene, in stops. Negative for a smaller sensor.\n */\n totalLightDifferenceStops: number;\n /** Angles of view of this lens on the sensor (the equivalent lens gives the same diagonal angle). */\n fieldOfView: SensorEquivalenceFieldOfView;\n /** Exact or approximated, with the conditions, for every quantity above. */\n basis: Readonly<Record<SensorEquivalenceQuantity, EquivalenceValueBasis>>;\n}\n\nconst SENSOR_EQUIVALENCE_BASIS: Readonly<Record<SensorEquivalenceQuantity, EquivalenceValueBasis>> = Object.freeze({\n cropFactor: Object.freeze({\n kind: \"exact\",\n statement: \"Ratio of the reference diagonal to the sensor diagonal (diagonal convention).\"\n }),\n equivalentFocalLengthMm: Object.freeze({\n kind: \"exact\",\n statement: \"Same diagonal angle of view on the reference format, for an ideal rectilinear lens focused at infinity. \" +\n \"Horizontal and vertical angles match too only when the aspect ratios match.\"\n }),\n equivalentFNumber: Object.freeze({\n kind: \"approximation\",\n statement: \"Same depth of field and background blur as a share of the frame, at the same subject distance, framing and output size, \" +\n \"and the same entrance-pupil diameter (so the same total light at the same shutter time). In the thin-lens defocus equation it is exact \" +\n \"only when the focus distance s is much greater than the focal length f; with focal lengths in the ratio cropFactor, the blur shares match \" +\n \"at cropFactor·N·(s − f)/(s − cropFactor·f). \" +\n \"Total light matches exactly only when the aspect ratios match. It does not set exposure.\"\n }),\n exposureFNumber: Object.freeze({\n kind: \"exact\",\n statement: \"Image illuminance is proportional to 1/N², independent of sensor size (ideal lens; transmission, vignetting and \" +\n \"close-focus bellows factor excluded). Equivalence never changes the exposure setting.\"\n }),\n entrancePupilDiameterMm: Object.freeze({\n kind: \"exact\",\n statement: \"Entrance-pupil diameter D = f/N by the definition of the f-number. The equivalent lens (cropFactor·f at cropFactor·N) has the same D.\"\n }),\n areaRatio: Object.freeze({\n kind: \"exact\",\n statement: \"Product of width and height of the sensor divided by that of the reference.\"\n }),\n totalLightDifferenceStops: Object.freeze({\n kind: \"exact\",\n statement: \"log2 of the area ratio: at the same f-number, shutter time and scene the image illuminance is the same, so total light scales \" +\n \"with area (uniform illuminance; vignetting excluded).\"\n }),\n fieldOfView: Object.freeze({\n kind: \"exact\",\n statement: \"2·atan(dimension / 2f) for an ideal rectilinear lens focused at infinity; distortion and focus breathing excluded.\"\n })\n});\n\nfunction requireImagingArea(area: unknown, label: string): asserts area is SensorImagingArea {\n requireInputObject(area, label);\n requireInputKeys(area, label, [\"widthMm\", \"heightMm\"]);\n const record = area as SensorImagingArea;\n requirePositiveFinite(`${label}.widthMm`, record.widthMm);\n requirePositiveFinite(`${label}.heightMm`, record.heightMm);\n}\n\n/** √((rw² + rh²)/(w² + h²)), with only correctly rounded operations when nothing overflows (portable). */\nfunction diagonalRatio(reference: SensorImagingArea, sensor: SensorImagingArea): number {\n const referenceSquared = reference.widthMm * reference.widthMm + reference.heightMm * reference.heightMm;\n const sensorSquared = sensor.widthMm * sensor.widthMm + sensor.heightMm * sensor.heightMm;\n const ratio = referenceSquared / sensorSquared;\n return Number.isFinite(referenceSquared) && Number.isFinite(sensorSquared) && sensorSquared > 0 && ratio > 0 && Number.isFinite(ratio)\n ? Math.sqrt(ratio)\n : portableHypot(reference.widthMm, reference.heightMm) / portableHypot(sensor.widthMm, sensor.heightMm);\n}\n\nfunction rectilinearAngleDegrees(dimensionMm: number, focalLengthMm: number): number {\n return (2 * Math.atan(dimensionMm / (2 * focalLengthMm)) * 180) / Math.PI;\n}\n\n/**\n * Equivalence of a lens (focal length, f-number) on one sensor against a reference format,\n * 36 × 24 mm by default: crop factor, equivalent focal length, equivalent f-number for depth of\n * field, blur and total light, the unchanged f-number for exposure, sensor area ratio, total-light\n * difference in stops and the angles of view.\n *\n * Call it once per sensor for a lens table. Every quantity carries a `basis` entry saying whether it\n * is exact under its stated conditions or an approximation. The equivalent f-number is an\n * approximation (exact when the focus distance is much greater than the focal length) and never an\n * exposure setting: exposure uses `exposureFNumber`, the f-number on the lens.\n *\n * Example: 50 mm f/2 on a 23.5 × 15.6 mm sensor gives crop factor ≈ 1.53, ≈ 77 mm and ≈ f/3.1.\n *\n * @param input Sensor active area, focal length, f-number and optional reference format.\n * @returns The equivalence numbers with a per-quantity exactness statement.\n */\nexport function calculateSensorEquivalence(\n input: CalculateSensorEquivalenceInput\n): CalculationResult<SensorEquivalence> {\n requireInputKeys(input, \"calculateSensorEquivalence\", [\"sensor\", \"focalLengthMm\", \"fNumber\", \"reference\"]);\n requireImagingArea(input.sensor, \"sensor\");\n requirePositiveFinite(\"focalLengthMm\", input.focalLengthMm);\n requirePositiveFinite(\"fNumber\", input.fNumber);\n if (input.reference !== undefined) requireImagingArea(input.reference, \"reference\");\n\n const sensor: SensorImagingArea = { widthMm: input.sensor.widthMm, heightMm: input.sensor.heightMm };\n const reference: SensorImagingArea = input.reference === undefined\n ? { ...REFERENCE_FULL_FRAME }\n : { widthMm: input.reference.widthMm, heightMm: input.reference.heightMm };\n // The default reference reuses the canonical 35 mm crop factor, so it is bit-identical to\n // calculateEquivalentFocalLength35Mm and calculateImagingAreaMetrics.\n const cropFactor = input.reference === undefined\n ? calculateImagingAreaMetrics(sensor).value.cropFactor35Mm\n : diagonalRatio(reference, sensor);\n const areaRatio = (sensor.widthMm / reference.widthMm) * (sensor.heightMm / reference.heightMm);\n const sensorDiagonalMm = portableHypot(sensor.widthMm, sensor.heightMm);\n if (!(areaRatio > 0) || !Number.isFinite(areaRatio)) {\n throw new InvalidScientificInputError(\"sensor and reference areas must differ by a ratio that is a finite number greater than zero.\");\n }\n\n return approximationResult(\n {\n focalLengthMm: input.focalLengthMm,\n fNumber: input.fNumber,\n sensor,\n reference,\n sensorDiagonalMm,\n referenceDiagonalMm: portableHypot(reference.widthMm, reference.heightMm),\n cropFactor,\n equivalentFocalLengthMm: input.focalLengthMm * cropFactor,\n equivalentFNumber: input.fNumber * cropFactor,\n exposureFNumber: input.fNumber,\n entrancePupilDiameterMm: input.focalLengthMm / input.fNumber,\n areaRatio,\n totalLightDifferenceStops: portableLog2(areaRatio),\n fieldOfView: {\n horizontalDegrees: rectilinearAngleDegrees(sensor.widthMm, input.focalLengthMm),\n verticalDegrees: rectilinearAngleDegrees(sensor.heightMm, input.focalLengthMm),\n diagonalDegrees: rectilinearAngleDegrees(sensorDiagonalMm, input.focalLengthMm)\n },\n basis: SENSOR_EQUIVALENCE_BASIS\n },\n \"sensor-equivalence\",\n \"1.0.0\",\n [\n \"Crop factor and equivalence are diagonal-based against the reference format (36 × 24 mm unless given).\",\n \"Equivalent f-number concerns depth of field, background blur and total light for the same framing and output size; it is not an exposure setting.\",\n \"Equivalent f-number is a thin-lens approximation, exact when the focus distance is much greater than the focal length.\",\n \"Exposure f-number is the f-number on the lens: image illuminance does not depend on sensor size.\",\n \"Ideal rectilinear lens focused at infinity; distortion, vignetting, transmission and focus breathing excluded.\",\n \"Physical focal length and f-number are not changed; see basis for each quantity.\"\n ]\n );\n}\n\nexport interface CalculateBlurShareOfFrameInput {\n /** Blur-circle diameter on the sensor in millimetres (for example from calculateDefocusCircle). Zero is allowed. */\n blurCircleMm: number;\n /** Physical active imaging area of the sensor. */\n sensor: SensorImagingArea;\n /**\n * Optional print size. The whole frame is enlarged to fit inside it without cropping, long side\n * to long side.\n */\n print?: { widthMm: number; heightMm: number };\n}\n\nexport interface BlurShareOfPrint {\n /** Linear enlargement from sensor to print: the smaller of the long-side and short-side ratios. */\n enlargement: number;\n /** Blur-circle diameter on the print in millimetres: blurCircleMm × enlargement. */\n blurOnPrintMm: number;\n /** Size of the printed image in millimetres (equal to the print when the aspect ratios match). */\n imageLongSideMm: number;\n imageShortSideMm: number;\n}\n\nexport interface BlurShareOfFrame {\n /** Blur-circle diameter on the sensor in millimetres. */\n blurCircleMm: number;\n /** Sensor diagonal in millimetres. */\n frameDiagonalMm: number;\n /** Blur diameter / frame diagonal. Equal shares look the same at the same output size. */\n shareOfDiagonal: number;\n /** Blur diameter / frame width. */\n shareOfWidth: number;\n /** Blur diameter / frame height. */\n shareOfHeight: number;\n /** Blur on the print; present only when a print size was given. */\n print?: BlurShareOfPrint;\n}\n\n/**\n * A blur circle on the sensor as a share of the frame (diagonal, width and height) and, when a\n * print size is given, its diameter on that print.\n *\n * Equal shares of the diagonal look the same when the images are shown at the same size, which\n * is how \"same framing\" comparisons across sensor sizes are stated. Every value is an exact ratio\n * or scaling; whether a blur is visible depends on viewing distance and acuity, which this\n * function does not judge.\n *\n * @param input Blur diameter in millimetres, sensor active area and optional print size.\n * @returns Shares of the frame, and the blur on the print when a print is given.\n */\nexport function calculateBlurShareOfFrame(\n input: CalculateBlurShareOfFrameInput\n): CalculationResult<BlurShareOfFrame> {\n requireInputKeys(input, \"calculateBlurShareOfFrame\", [\"blurCircleMm\", \"sensor\", \"print\"]);\n if (typeof input.blurCircleMm !== \"number\" || !Number.isFinite(input.blurCircleMm) || input.blurCircleMm < 0) {\n throw new InvalidScientificInputError(\"blurCircleMm must be a finite number greater than or equal to zero.\");\n }\n requireImagingArea(input.sensor, \"sensor\");\n if (input.print !== undefined) requireImagingArea(input.print, \"print\");\n\n const { widthMm, heightMm } = input.sensor;\n const blurCircleMm = input.blurCircleMm;\n const frameDiagonalMm = portableHypot(widthMm, heightMm);\n let print: BlurShareOfPrint | undefined;\n if (input.print !== undefined) {\n const sensorLong = Math.max(widthMm, heightMm), sensorShort = Math.min(widthMm, heightMm);\n const printLong = Math.max(input.print.widthMm, input.print.heightMm), printShort = Math.min(input.print.widthMm, input.print.heightMm);\n const enlargement = Math.min(printLong / sensorLong, printShort / sensorShort);\n if (!(enlargement > 0) || !Number.isFinite(enlargement)) {\n throw new InvalidScientificInputError(\"print enlargement must be a finite number greater than zero.\");\n }\n print = {\n enlargement,\n blurOnPrintMm: blurCircleMm * enlargement,\n imageLongSideMm: sensorLong * enlargement,\n imageShortSideMm: sensorShort * enlargement\n };\n }\n\n return calculatedResult(\n {\n blurCircleMm,\n frameDiagonalMm,\n shareOfDiagonal: blurCircleMm / frameDiagonalMm,\n shareOfWidth: blurCircleMm / widthMm,\n shareOfHeight: blurCircleMm / heightMm,\n ...(print === undefined ? {} : { print })\n },\n \"blur-share-of-frame\",\n \"1.0.0\",\n [\n \"Shares are exact ratios of the blur diameter to the active frame's diagonal, width and height.\",\n \"A print shows the whole frame enlarged to fit inside it without cropping, long side to long side.\",\n \"Visibility of the blur depends on viewing distance and acuity; no acceptability criterion is applied.\"\n ]\n );\n}\n"]}
@@ -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":"AAAA,sCAAsC;AAEtC;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EACL,iBAAiB,EAElB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EACL,eAAe,EAEhB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,IAAI,aAAa,EAAE,0BAA0B,IAAI,qBAAqB,EAAE,0BAA0B,IAAI,+BAA+B,EAAE,6BAA6B,IAAI,kCAAkC,EAAE,0BAA0B,IAAI,+BAA+B,EAAE,sBAAsB,IAAI,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAC9Z,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAElE,MAAM,CAAC,MAAM,oCAAoC,GAC/C,OAAgB,CAAC;AACnB,MAAM,CAAC,MAAM,2BAA2B,GACtC,OAAgB,CAAC;AAgNnB,MAAM,WAAW,GACf,IAAI,GAAG,CAAmB;IACxB,QAAQ;IACR,WAAW;IACX,eAAe;CAChB,CAAC,CAAC;AAEL,MAAM,kBAAkB,GACtB,IAAI,GAAG,CAAuB;IAC5B,gBAAgB;IAChB,kBAAkB;IAClB,UAAU;CACX,CAAC,CAAC;AAEL,MAAM,mBAAmB,GACvB,IAAI,GAAG,CAAwB;IAC7B,UAAU;IACV,cAAc;IACd,eAAe;IACf,oBAAoB;CACrB,CAAC,CAAC;AAEL,SAAS,mBAAmB,CAC1B,KAAc,EACd,IAAY,EACZ,OAAuB;IAEvB,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,KAAK,CAAC,EAClB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,6BAA6B,CAChC,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GACV,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACzB,IACE,OAAO,KAAK;YACV,QAAQ;YACV,CAAC,OAAO,CAAC,GAAG,CAAC,KAAU,CAAC,EACxB,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,GAAG;gBACH,KAAK;gBACL,eAAe,CAClB,CAAC;QACJ,CAAC;QACD,OAAO,KAAU,CAAC;IACpB,CAAC,CAAC,CAAC;IACL,IACE,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI;QACpB,MAAM,CAAC,MAAM,EACb,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,+BAA+B,CAClC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAc;IAEd,MAAM,MAAM,GACV,aAAa,CACX,KAAK,EACL,qBAAqB,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,UAAU,EAAE,4BAA4B,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,UAAU,EAAE,aAAa,CAAC,CACjN,CAAC;IAEJ,IACE,MAAM,CAAC,aAAa;QACpB,oCAAoC,EACpC,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,6CAA6C;YAC3C,oCAAoC;YACpC,IAAI,CACP,CAAC;IACJ,CAAC;IACD,IACE,MAAM,CAAC,gBAAgB;QACvB,eAAe,EACf,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,+DAA+D,CAChE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GACZ,aAAa,CACX,MAAM,CAAC,QAAQ,EACf,8BAA8B,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CACrD,CAAC;IACJ,IACE,QAAQ,CAAC,IAAI;QACb,qBAAqB,EACrB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,kFAAkF,CACnF,CAAC;IACJ,CAAC;IACD,IACE,MAAM;SACH,0BAA0B;QAC7B,gDAAgD,EAChD,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,0GAA0G,CAC3G,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa,EACX,oCAAoC;QACtC,SAAS,EACP,qBAAqB,CACnB,MAAM,CAAC,SAAS,EAChB,+BAA+B,CAChC;QACH,cAAc,EACZ,qBAAqB,CACnB,MAAM,CAAC,cAAc,EACrB,oCAAoC,CACrC;QACH,gBAAgB,EACd,eAAe;QACjB,cAAc,EACZ,mBAAmB,CACjB,MAAM,CAAC,cAAc,EACrB,oCAAoC,EACpC,WAAW,CACZ;QACH,QAAQ,EAAE;YACR,IAAI,EACF,qBAAqB;YACvB,QAAQ,EACN,iBAAiB,CACf,QAAQ,CAAC,QAAQ,EACjB,uCAAuC,CACxC;SACJ;QACD,0BAA0B,EACxB,gDAAgD;QAClD,0BAA0B,EACxB,mBAAmB,CACjB,MAAM;aACH,0BAA0B,EAC7B,gDAAgD,EAChD,kBAAkB,CACnB;QACH,QAAQ,EACN,iBAAiB,CACf,MAAM,CAAC,QAAQ,EACf,8BAA8B,CAC/B;QACH,WAAW,EACT,gBAAgB,CACd,MAAM,CAAC,WAAW,EAClB,iCAAiC,CAClC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,KACmB;IAEnB,OAAO,UAAU,CACf,SAAS,CAAC,KAAK,CAAC,CACjB,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAChB,OAAe,EACf,OACqB,EACrB,IACkB,EAClB,KACY,EACZ,gBACuB,EACvB,gBAAwB,EACxB,sBACQ,EACR,cACQ,EACR,iBACQ,EACR,IAC2B;IAE3B,OAAO,cAAc,CAAC;QACpB,OAAO,EACL,2BAA2B;QAC7B,OAAO,EACL,+BAA+B,CAC7B,OAAO,EACP,SAAS,CACV;QACH,aAAa,EAAE;YACb,SAAS,EACP,OAAO,CAAC,SAAS;YACnB,cAAc,EACZ,OAAO,CAAC,cAAc;SACzB;QACD,kBAAkB,EAAE;YAClB,GAAG,OAAO,CAAC,WAAW;SACvB;QACD,IAAI;QACJ,gBAAgB;QAChB,KAAK,EACH,eAAe,CAAC,KAAK,CAAC;QACxB,GAAG,CAAC,cAAc;YAClB,SAAS;YACP,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,cAAc;aACf,CAAC;QACN,GAAG,CAAC,iBAAiB;YACrB,SAAS;YACP,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,iBAAiB;aAClB,CAAC;QACN,oBAAoB,EAClB,gBAAgB;QAClB,sBAAsB;QACtB,MAAM,EACJ,gBAAgB;YAChB,QAAQ;QACV,GAAG,CAAC,IAAI,KAAK,SAAS;YACpB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,IAAI;aACL,CAAC;QACN,aAAa,EACX,qBAAqB;QACvB,0BAA0B,EACxB,OAAO;aACJ,0BAA0B;QAC/B,gBAAgB,EAAE,KAAK;QACvB,gBAAgB,EAAE,KAAK;QACvB,oBAAoB,EAClB,KAAK;QACP,iCAAiC,EAC/B,KAAK;QACP,2BAA2B,EACzB,KAAK;QACP,WAAW,EAAE;YACX,GAAG,OAAO,CAAC,WAAW;YACtB,sGAAsG;YACtG,yJAAyJ;YACzJ,yFAAyF;SAC1F;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,oBAAoB,CAC3B,OACqB,EACrB,IACkB;IAElB,IACE,CAAC,OAAO,CAAC,cAAc;SACpB,QAAQ,CAAC,IAAI,CAAC,EACjB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,8DAA8D,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAChC,OACmB,EACnB,gBAAwB;IAExB,MAAM,MAAM,GACV,kCAAkC,CAChC,gBAAgB,EAChB,kBAAkB,CACnB,CAAC;IACJ,IACE,MAAM;QACN,OAAO,CAAC,oBAAoB,EAC5B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB,CACzB,KACmB,EACnB,OACqB;IAErB,IACE,KAAK,CAAC,aAAa;SAChB,SAAS;QACV,OAAO,CAAC,SAAS;QACnB,KAAK,CAAC,aAAa;aAChB,cAAc;YACf,OAAO,CAAC,cAAc,EACxB,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,sDAAsD,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAC8B;IAE9B,gBAAgB,CAAC,KAAK,EAAE,yBAAyB,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACzH,MAAM,OAAO,GACX,wBAAwB,CACtB,KAAK,CAAC,OAAO,CACd,CAAC;IACJ,oBAAoB,CAClB,OAAO,EACP,KAAK,CAAC,IAAI,CACX,CAAC;IACF,MAAM,IAAI,GACR,KAAK,CAAC,kBAAkB;QACxB,SAAS;QACP,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,kCAAkC,CAChC,KAAK,CAAC,kBAAkB,EACxB,oBAAoB,CACrB,CAAC;IAER,OAAO,SAAS,CACd,KAAK,CAAC,OAAO,EACb,OAAO,EACP,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,YAAY,EAClB,MAAM,EACN,IAAI,EACJ,IAAI,CACL,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,KACmB;IAEnB,OAAO;QACL,aAAa,EACX,oCAAoC;QACtC,SAAS,EACP,KAAK,CAAC,aAAa;aAChB,SAAS;QACd,cAAc,EACZ,KAAK,CAAC,aAAa;aAChB,cAAc;QACnB,gBAAgB,EACd,eAAe;QACjB,cAAc,EAAE;YACd,KAAK,CAAC,IAAI;SACX;QACD,QAAQ,EAAE;YACR,IAAI,EACF,qBAAqB;YACvB,QAAQ,EAAE,EAAE;SACb;QACD,0BAA0B,EACxB,KAAK;aACF,0BAA0B;QAC/B,0BAA0B,EAAE;YAC1B,gBAAgB;YAChB,kBAAkB;YAClB,UAAU;SACX;QACD,QAAQ,EAAE,EAAE;QACZ,WAAW,EACT,KAAK,CAAC,kBAAkB;KAC3B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAC0B;IAE1B,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC;IAC/G,kBAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzC,IACE,KAAK,CAAC,KAAK,CAAC,IAAI;QAChB,QAAQ,EACR,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,iDAAiD,CAClD,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,2BAA2B,CACnC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,IACE,KAAK,CAAC,SAAS;QACf,SAAS,EACT,CAAC;QACD,+BAA+B,CAC7B,KAAK,CAAC,SAAS,EACf,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GACR,yBAAyB,CACvB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,gBAAgB,CACvB,CAAC;IAEJ,OAAO,SAAS,CACd,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,QAAQ,EACR,KAAK,CAAC,KAAK,EACX,MAAM,EACN,IAAI,EACJ,IAAI,CACL,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,WACwB;IAExB,MAAM,QAAQ,GACZ,+BAA+B,CAC7B,WAAW,CAAC,QAAQ,EACpB,sBAAsB,CACvB,CAAC;IACJ,MAAM,iBAAiB,GACrB,kCAAkC,CAChC,WAAW;SACR,iBAAiB,EACpB,+BAA+B,CAChC,CAAC;IACJ,MAAM,WAAW,GACf,WAAW,CAAC,WAAW;QACvB,SAAS;QACP,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,+BAA+B,CAC7B,WAAW,CAAC,WAAW,EACvB,yBAAyB,CAC1B,CAAC;IAER,IACE,WAAW,CAAC,IAAI;QAChB,gBAAgB,EAChB,CAAC;QACD,OAAO;YACL,IAAI,EAAE,gBAAgB;YACtB,QAAQ;YACR,GAAG,CAAC,WAAW;gBACf,SAAS;gBACP,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,WAAW;iBACZ,CAAC;YACN,iBAAiB;YACjB,qBAAqB,EACnB,+BAA+B,CAC7B,WAAW;iBACR,qBAAqB,EACxB,mCAAmC,CACpC;SACJ,CAAC;IACJ,CAAC;IAED,IACE,WAAW,CAAC,IAAI;QAChB,sBAAsB,EACtB,CAAC;QACD,OAAO;YACL,IAAI,EACF,sBAAsB;YACxB,QAAQ;YACR,GAAG,CAAC,WAAW;gBACf,SAAS;gBACP,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,WAAW;iBACZ,CAAC;YACN,iBAAiB;SAClB,CAAC;IACJ,CAAC;IAED,IACE,WAAW,CAAC,IAAI;QACd,aAAa;QACf,mBAAmB,CAAC,GAAG,CACrB,WAAW,CAAC,MAAM,CACnB,EACD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,QAAQ;YACR,GAAG,CAAC,WAAW;gBACf,SAAS;gBACP,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,WAAW;iBACZ,CAAC;YACN,iBAAiB;YACjB,MAAM,EACJ,WAAW,CAAC,MAAM;SACrB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,qCAAqC,CACtC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,WAMG;IAEH,IACE,WAAW,CAAC,IAAI;QAChB,sBAAsB,EACtB,CAAC;QACD,OAAO;YACL,IAAI,EAAE,UAAU;SACjB,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,SAAS,EACP,WAAW;aACR,qBAAqB;KAC3B,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,CAAa,EACb,CAAa;IAEb,IACE,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EACjB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IACE,CAAC,CAAC,IAAI,KAAK,UAAU;QACrB,CAAC,CAAC,IAAI,KAAK,UAAU,EACrB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CACL,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CACZ,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAC9B,KACoC,EACpC,WAMG;IAEH,MAAM,KAAK,GACT,oBAAoB,CAClB,WAAW,CACZ,CAAC;IACJ,MAAM,KAAK,GACT,SAAS,CACP,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,KAAK,CAAC,KAAK,CAAC,IAAI,EAChB,KAAK,EACL,UAAU,EACV,WAAW;SACR,iBAAiB,EACpB,WAAW;SACR,iBAAiB,EACpB,WAAW,CAAC,QAAQ,EACpB,WAAW,CAAC,WAAW,CACxB,CAAC;IACJ,OAAO;QACL,KAAK;QACL,wBAAwB,EACtB,IAAI;QACN,YAAY,EACV,CAAC,WAAW,CACV,KAAK,CAAC,KAAK,CAAC,KAAK,EACjB,KAAK,CACN;QACH,gBAAgB,EACd,KAAK,CAAC,KAAK;QACb,aAAa,EACX,qBAAqB;QACvB,0BAA0B,EACxB,KAAK;KACR,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAC1B,KACoC;IAEpC,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,wBAAwB,EACtB,KAAK;QACP,YAAY,EAAE,KAAK;QACnB,gBAAgB,EACd,KAAK,CAAC,KAAK;QACb,aAAa,EACX,qBAAqB;QACvB,0BAA0B,EACxB,KAAK;KACR,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,6BAA6B,CAC3C,KACoC;IAEpC,gBAAgB,CAAC,KAAK,EAAE,+BAA+B,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACvG,IACE,KAAK,CAAC,KAAK,KAAK,SAAS;QACzB,KAAK,CAAC,KAAK,KAAK,QAAQ,EACxB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD,IACE,KAAK,CAAC,KAAK,CAAC,IAAI;QAChB,QAAQ,EACR,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GACf,gBAAgB,CACd,KAAK,CAAC,WAAW,CAClB,CAAC;IACJ,yBAAyB,CACvB,KAAK,CAAC,KAAK,EACX,WAAW;SACR,iBAAiB,CACrB,CAAC;IAEF,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,mBAAmB,CACxB,KAAK,CACN,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,KAAK,CAAC,IAAI;QAChB,WAAW,EACX,CAAC;QACD,IACE,KAAK,CAAC,KAAK;YACT,QAAQ;YACV,KAAK,CAAC,KAAK;iBACR,gBAAgB;gBACjB,UAAU,EACZ,CAAC;YACD,OAAO,mBAAmB,CACxB,KAAK,CACN,CAAC;QACJ,CAAC;QACD,IACE,KAAK,CAAC,KAAK;YACX,SAAS,EACT,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,IACE,WAAW,CAAC,IAAI;YAChB,aAAa,EACb,CAAC;YACD,MAAM,KAAK,GACT,SAAS,CACP,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,WAAW,EACX,KAAK,CAAC,KAAK,CAAC,KAAK,EACjB,aAAa,EACb,WAAW;iBACR,iBAAiB,EACpB,KAAK,CAAC,KAAK;iBACR,sBAAsB,EACzB,WAAW,CAAC,QAAQ,EACpB,WAAW,CAAC,WAAW,CACxB,CAAC;YACJ,OAAO;gBACL,KAAK;gBACL,wBAAwB,EACtB,IAAI;gBACN,YAAY,EAAE,KAAK;gBACnB,gBAAgB,EACd,KAAK,CAAC,KAAK;gBACb,aAAa,EACX,qBAAqB;gBACvB,0BAA0B,EACxB,KAAK;aACR,CAAC;QACJ,CAAC;QACD,OAAO,uBAAuB,CAC5B,KAAK,EACL,WAAW,CACZ,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,KAAK;QACX,QAAQ,EACR,CAAC;QACD,IACE,KAAK,CAAC,KAAK;aACR,cAAc;YACjB,SAAS,EACT,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,kEAAkE,CACnE,CAAC;QACJ,CAAC;QACD,IACE,WAAW,CAAC,QAAQ;YACpB,KAAK,CAAC,KAAK;iBACR,cAAc,EACjB,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,kGAAkG,CACnG,CAAC;QACJ,CAAC;QACD,IACE,KAAK,CAAC,KAAK;aACR,gBAAgB;YACnB,aAAa,EACb,CAAC;YACD,MAAM,IAAI,2BAA2B,CACnC,oFAAoF,CACrF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IACE,WAAW,CAAC,IAAI;QAChB,aAAa,EACb,CAAC;QACD,MAAM,KAAK,GACT,SAAS,CACP,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,eAAe,EACf,KAAK,CAAC,KAAK,CAAC,KAAK,EACjB,aAAa,EACb,WAAW;aACR,iBAAiB,EACpB,KAAK,CAAC,KAAK;aACR,sBAAsB,EACzB,WAAW,CAAC,QAAQ,EACpB,WAAW,CAAC,WAAW,CACxB,CAAC;QACJ,OAAO;YACL,KAAK;YACL,wBAAwB,EACtB,IAAI;YACN,YAAY,EAAE,KAAK;YACnB,gBAAgB,EACd,KAAK,CAAC,KAAK;YACb,aAAa,EACX,qBAAqB;YACvB,0BAA0B,EACxB,KAAK;SACR,CAAC;IACJ,CAAC;IAED,OAAO,uBAAuB,CAC5B,KAAK,EACL,WAAW,CACZ,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,KACmB;IAEnB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC5F,MAAM,IAAI,GACR,yBAAyB,CACvB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,gBAAgB,CACvB,CAAC;IAEJ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,CAAC;QACD,OAAO,SAAS,CACd,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,KAAK,CAAC,KAAK,CAAC,IAAI,EAChB,KAAK,CAAC,KAAK,CAAC,KAAK,EACjB,QAAQ,EACR,IAAI,EACJ,KAAK,CAAC,KAAK;aACR,sBAAsB,EACzB,KAAK,CAAC,KAAK;aACR,cAAc,EACjB,KAAK,CAAC,KAAK;aACR,iBAAiB,EACpB;YACE,eAAe,EACb,IAAI;YACN,sBAAsB,EACpB,KAAK,CAAC,KAAK;iBACR,gBAGA;SACN,CACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,MAAM,MAAM,GACV,KAAK,CAAC,KAAK,CAAC,IAAI;QACd,EAAE,sBAAsB,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,2BAA2B,CACnC,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CACd,KAAK,CAAC,OAAO,EACb,gBAAgB,CACd,KAAK,CAAC,KAAK,CACZ,EACD,KAAK,CAAC,KAAK,CAAC,IAAI,EAChB,KAAK,CAAC,KAAK,CAAC,KAAK,EACjB,MAAM,EACN,IAAI,EACJ,KAAK,CAAC,KAAK;SACR,sBAAsB,EACzB,KAAK,CAAC,KAAK;SACR,cAAc,EACjB,KAAK,CAAC,KAAK;SACR,iBAAiB,CACrB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAC6B;IAE7B,gBAAgB,CAAC,KAAK,EAAE,wBAAwB,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IACpF,MAAM,OAAO,GACX,wBAAwB,CACtB,KAAK,CAAC,OAAO,CACd,CAAC;IACJ,kBAAkB,CAChB,KAAK,CAAC,KAAK,EACX,OAAO,CACR,CAAC;IAEF,IACE,CAAC,OAAO;SACL,0BAA0B;SAC1B,QAAQ,CACP,KAAK,CAAC,QAAQ,CACf,EACH,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,QAAQ;QACd,kBAAkB,EAClB,CAAC;QACD,OAAO;YACL,QAAQ,EACN,KAAK,CAAC,QAAQ;YAChB,iBAAiB,EAAE,IAAI;YACvB,MAAM,EACJ,sCAAsC;YACxC,qCAAqC,EACnC,KAAK;YACP,oBAAoB,EAClB,KAAK;YACP,gBAAgB,EACd,KAAK;SACR,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,QAAQ;QACd,UAAU,EACV,CAAC;QACD,OAAO;YACL,QAAQ,EACN,KAAK,CAAC,QAAQ;YAChB,iBAAiB,EACf,KAAK;YACP,MAAM,EACJ,6BAA6B;YAC/B,qCAAqC,EACnC,IAAI;YACN,oBAAoB,EAClB,KAAK;YACP,gBAAgB,EACd,KAAK;SACR,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,KAAK,CAAC,IAAI;QAChB,QAAQ,EACR,CAAC;QACD,OAAO;YACL,QAAQ,EACN,KAAK,CAAC,QAAQ;YAChB,iBAAiB,EAAE,IAAI;YACvB,MAAM,EACJ,oBAAoB;YACtB,qCAAqC,EACnC,KAAK;YACP,oBAAoB,EAClB,KAAK;YACP,gBAAgB,EACd,KAAK;SACR,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,KAAK;SACR,gBAAgB;QACnB,QAAQ,EACR,CAAC;QACD,OAAO;YACL,QAAQ,EACN,KAAK,CAAC,QAAQ;YAChB,iBAAiB,EAAE,IAAI;YACvB,MAAM,EACJ,cAAc;YAChB,qCAAqC,EACnC,KAAK;YACP,oBAAoB,EAClB,KAAK;YACP,gBAAgB,EACd,KAAK;SACR,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,KAAK;SACR,gBAAgB;QACnB,UAAU,EACV,CAAC;QACD,OAAO;YACL,QAAQ,EACN,KAAK,CAAC,QAAQ;YAChB,iBAAiB,EAAE,IAAI;YACvB,MAAM,EACJ,gBAAgB;YAClB,qCAAqC,EACnC,KAAK;YACP,oBAAoB,EAClB,KAAK;YACP,gBAAgB,EACd,KAAK;SACR,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EACN,KAAK,CAAC,QAAQ;QAChB,iBAAiB,EAAE,KAAK;QACxB,MAAM,EACJ,oBAAoB;QACtB,qCAAqC,EACnC,KAAK;QACP,oBAAoB,EAClB,KAAK;QACP,gBAAgB,EACd,KAAK;KACR,CAAC;AACJ,CAAC","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
  }